package com.jrx.anytxn.transaction.handler.impl;

import com.alibaba.fastjson.JSON;
import com.jrx.anytxn.common.data.TxnRespCode;
import com.jrx.anytxn.common.exception.TxnBizException;
import com.jrx.anytxn.common.exception.TxnException;
import com.jrx.anytxn.common.exception.TxnRuntimeException;
import com.jrx.anytxn.common.uitl.BeanMapping;
import com.jrx.anytxn.common.uitl.CollectorsUtils;
import com.jrx.anytxn.common.uitl.DateUtils;
import com.jrx.anytxn.common.uitl.StringUtils;
import com.jrx.anytxn.customer.constant.CouponConstant;
import com.jrx.anytxn.customer.constant.CustomerConstant;
import com.jrx.anytxn.customer.dto.coupon.CouponReq;
import com.jrx.anytxn.customer.dto.customer.CustomerRes;
import com.jrx.anytxn.customer.dto.customer.medical.CustomerUseLimitFrozeFrozeReq;
import com.jrx.anytxn.customer.entity.CmCustomerBankcardInfo;
import com.jrx.anytxn.customer.entity.CmCustomerExtInfo;
import com.jrx.anytxn.customer.entity.CmCustomerLimitInfo;
import com.jrx.anytxn.customer.entity.CmCustomerRightInfo;
import com.jrx.anytxn.customer.entity.CmCustomerSecondLevel;
import com.jrx.anytxn.customer.mapper.ext.ExtCmCustomerExtInfoMapper;
import com.jrx.anytxn.customer.mapper.ext.ExtCmCustomerLimitInfoMapper;
import com.jrx.anytxn.customer.service.IBankCardService;
import com.jrx.anytxn.customer.service.ICustomerLimitInfoService;
import com.jrx.anytxn.customer.service.ICustomerRightService;
import com.jrx.anytxn.customer.service.ICustomerService;
import com.jrx.anytxn.customer.service.impl.ICmCustomerLimitFrozeServiceImpl;
import com.jrx.anytxn.feign.api.service.IUserApiService;
import com.jrx.anytxn.feign.investor.request.InvestorCancelLoanReq;
import com.jrx.anytxn.feign.investor.service.IAnyTxnInvestorService;
import com.jrx.anytxn.feign.param.request.PartnerLimitAdjustmentReq;
import com.jrx.anytxn.feign.param.response.PartnerLimitAdjustmentRes;
import com.jrx.anytxn.feign.param.service.IAnyTxnParamService;
import com.jrx.anytxn.i18n.response.TxnRespResult;
import com.jrx.anytxn.number.exception.SegmentException;
import com.jrx.anytxn.number.service.ISegmentService;
import com.jrx.anytxn.param.constant.Constant;
import com.jrx.anytxn.param.constant.ErrorMsgConstant;
import com.jrx.anytxn.param.dto.aviator.RuleMatchReq;
import com.jrx.anytxn.param.dto.product.InterestTableRes;
import com.jrx.anytxn.param.entity.PmMerchantTable;
import com.jrx.anytxn.param.entity.PmPartnerInfo;
import com.jrx.anytxn.param.entity.PrAmortMethod;
import com.jrx.anytxn.param.entity.PrBlockCode;
import com.jrx.anytxn.param.entity.PrChannelTable;
import com.jrx.anytxn.param.entity.PrInterestTable;
import com.jrx.anytxn.param.entity.PrOrganizationTable;
import com.jrx.anytxn.param.entity.PrPenaltyRateTable;
import com.jrx.anytxn.param.entity.PrProductTable;
import com.jrx.anytxn.param.entity.PrWaiveInterestTable;
import com.jrx.anytxn.param.enums.BizTypeEnum;
import com.jrx.anytxn.param.mapper.ext.ExtPmMerchantTableMapper;
import com.jrx.anytxn.param.service.aviator.IAviatorService;
import com.jrx.anytxn.param.service.limit.ICreditLimitMappingService;
import com.jrx.anytxn.param.service.partner.IPmMerchantTableService;
import com.jrx.anytxn.param.service.partner.IPmPartnerInfoService;
import com.jrx.anytxn.param.service.product.IAmortMethodService;
import com.jrx.anytxn.param.service.product.IInterestTableService;
import com.jrx.anytxn.param.service.product.IPrBlockCodeService;
import com.jrx.anytxn.param.service.product.IPrPenaltyRateTableService;
import com.jrx.anytxn.param.service.product.IPrWaiveInterestTableService;
import com.jrx.anytxn.param.service.product.IProductTableService;
import com.jrx.anytxn.param.service.system.IChannelTableService;
import com.jrx.anytxn.param.service.system.IOrganizationTableService;
import com.jrx.anytxn.param.service.system.ISequenceNumberService;
import com.jrx.anytxn.param.utils.BizTypeProducts;
import com.jrx.anytxn.transaction.bean.*;
import com.jrx.anytxn.transaction.constant.TransBizConstant;
import com.jrx.anytxn.transaction.constant.TransErrMsgConstant;
import com.jrx.anytxn.transaction.dto.common.CommonAddReq;
import com.jrx.anytxn.transaction.dto.common.CommonReq;
import com.jrx.anytxn.transaction.dto.common.CommonRes;
import com.jrx.anytxn.transaction.dto.common.DisbursementLogRes;
import com.jrx.anytxn.transaction.dto.common.RepaymentPlanInfoRes;
import com.jrx.anytxn.transaction.dto.loan.*;
import com.jrx.anytxn.transaction.entity.AmAccountMainInfo;
import com.jrx.anytxn.transaction.entity.AmInterestAccrualInfo;
import com.jrx.anytxn.transaction.entity.AmLoanAccountInfo;
import com.jrx.anytxn.transaction.entity.AmLoanMainInfo;
import com.jrx.anytxn.transaction.entity.AmRepaymentPlanInfo;
import com.jrx.anytxn.transaction.entity.AmWaiveRepaymentPlanInfo;
import com.jrx.anytxn.transaction.entity.TlCollectionLoanList;
import com.jrx.anytxn.transaction.entity.TlDisbursementLog;
import com.jrx.anytxn.transaction.entity.TlGlInterfaceInfo;
import com.jrx.anytxn.transaction.entity.TlInterestAccrualLog;
import com.jrx.anytxn.transaction.entity.TlLoanClassifyChange;
import com.jrx.anytxn.transaction.entity.TlLoanFailLog;
import com.jrx.anytxn.transaction.entity.TlLoanStagingMapping;
import com.jrx.anytxn.transaction.entity.TlPaymentAllocationLog;
import com.jrx.anytxn.transaction.entity.TlPaymentLog;
import com.jrx.anytxn.transaction.entity.TlRefundLog;
import com.jrx.anytxn.transaction.entity.TlTransactionInfo;
import com.jrx.anytxn.transaction.enums.DailyAccountEnum;
import com.jrx.anytxn.transaction.handler.ILoanHandler;
import com.jrx.anytxn.transaction.handler.IPaymentHandler;
import com.jrx.anytxn.transaction.mapper.base.TlCollectionLoanListMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtAmAccountMainInfoMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtAmInterestAccrualInfoMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtAmLoanAccountInfoMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtAmLoanMainInfoMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtAmRepaymentPlanInfoMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtAmWaiveRepaymentPlanInfoMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtTlCollectionLoanListMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtTlDisbursementLogMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtTlGlInterfaceInfoMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtTlInterestAccrualLogMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtTlLoanClassifyChangeMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtTlLoanFailLogMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtTlLoanStagingMappingMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtTlPaymentAllocationLogMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtTlPaymentLogMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtTlRefundLogMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtTlTransactionInfoMapper;
import com.jrx.anytxn.transaction.service.*;
import com.jrx.anytxn.transaction.service.impl.DebtCalculationServiceImpl;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.time.DateFormatUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Random;
import java.util.function.Function;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;

/**
 * 贷款handler实现
 *
 * @author zhenghaopu
 * date  2019/9/24 0024
 * @version V1.0
 **/
@Component
public class LoanHandlerImpl implements ILoanHandler {

    private static final Logger logger = LoggerFactory.getLogger(LoanHandlerImpl.class);

    private Random r = SecureRandom.getInstanceStrong();

    @Autowired
    private IChannelTableService channelTableService;
    @Autowired
    private IProductTableService productTableService;
    @Autowired
    private IOrganizationTableService organizationTableService;
    @Autowired
    private ICustomerLimitInfoService customerLimitInfoService;
    @Autowired
    private ICustomerService customerService;
    @Autowired
    private IBankCardService bankCardService;
    @Autowired
    private IInterestTableService interestTableService;
    @Autowired
    private IPrPenaltyRateTableService prPenaltyRateTableService;
    @Autowired
    private IAmortMethodService amortMethodService;
    @Autowired
    private IAviatorService aviatorService;
    @Autowired
    private ILoanService loanService;
    @Autowired
    private ILoanMainInfoService loanMainInfoService;
    @Autowired
    private ILoanAccountMainInfoService loanAccountMainInfoService;
    @Resource
    private ExtAmLoanMainInfoMapper extAmLoanMainInfoMapper;
    @Resource
    private ExtAmLoanAccountInfoMapper extAmLoanAccountInfoMapper;
    @Resource
    private ExtAmRepaymentPlanInfoMapper extAmRepaymentPlanInfoMapper;
    @Autowired
    private DebtCalculationServiceImpl debtCalculationService;
    @Autowired
    private IRepaymentPlanService repaymentPlanService;
    @Resource
    private ExtAmInterestAccrualInfoMapper extAmInterestAccrualInfoMapper;
    @Resource
    private ExtAmAccountMainInfoMapper extAmAccountMainInfoMapper;
    @Resource
    private ExtTlCollectionLoanListMapper extTlCollectionLoanListMapper;
    @Autowired
    private ISegmentService segmentService;
    @Resource
    private TlCollectionLoanListMapper tlCollectionLoanListMapper;
    @Autowired
    private IPrWaiveInterestTableService prWaiveInterestTableService;
    @Resource
    private ExtPmMerchantTableMapper extPmMerchantTableMapper;
    @Resource
    private ExtTlLoanStagingMappingMapper extTlLoanStagingMappingMapper;
    @Resource
    private ExtTlPaymentLogMapper extTlPaymentLogMapper;
    @Resource
    private ExtTlPaymentAllocationLogMapper extTlPaymentAllocationLogMapper;
    @Autowired
    private IPmMerchantTableService pmMerchantTableService;
    @Autowired
    private IPmPartnerInfoService pmPartnerInfoService;
    @Lazy
    @Autowired
    private IAnyTxnParamService anyTxnParamService;
    @Autowired
    private ICustBatchService custBatchService;
    @Autowired
    private ICustomerRightService customerRightService;
    @Autowired
    private IPrPenaltyRateTableService penaltyRateTableService;
    @Resource
    private ExtTlDisbursementLogMapper extTlDisbursementLogMapper;
    @Resource
    private ExtTlLoanFailLogMapper exttlloanfaillogmapper;
    @Resource
    private ExtCmCustomerLimitInfoMapper extCmCustomerLimitInfoMapper;
    @Autowired
    private ICreditLimitMappingService iCreditLimitMappingService;
    @Autowired
    private IPrBlockCodeService blockCodeService;
    @Autowired
    ISequenceNumberService sequenceNumberService;
    @Resource
    private ExtTlRefundLogMapper extTlRefundLogMapper;
    @Autowired
    private IActivityLoanService activityLoanService;
    @Autowired
    private IUserApiService iUserApiService;
    @Resource
    private ExtCmCustomerExtInfoMapper extCmCustomerExtInfoMapper;
    @Autowired
    private ICmCustomerLimitFrozeServiceImpl iCmCustomerLimitFrozeService;
    @Autowired
    private IDisbursementLogService disbursementLogService;
    @Autowired
    private ISmsService smsService;
    @Autowired
    private IPaymentHandler iPaymentHandler;
    @Autowired
    private ITlLoanClassifyService tlLoanClassifyService;
    @Lazy
    @Autowired
    private IAnyTxnInvestorService anyTxnInvestorService;
    @Autowired
    private IPartnerLimitRecoveryService partnerLimitRecoveryService;
    @Resource
    private ExtTlGlInterfaceInfoMapper extTlGlInterfaceInfoMapper;
    @Resource
    private ExtTlTransactionInfoMapper extTlTransactionInfoMapper;
    @Resource
    private ExtAmWaiveRepaymentPlanInfoMapper extAmWaiveRepaymentPlanInfoMapper;
    @Resource
    private ExtTlInterestAccrualLogMapper extTlInterestAccrualLogMapper;
    @Resource
    private ExtTlLoanClassifyChangeMapper extTlLoanClassifyChangeMapper;

    public LoanHandlerImpl() throws NoSuchAlgorithmException {
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public LoanConfirmRes loanConfirmHandler(LoanConfirmReq loanConfirmReq, CustomerRes customer) throws Exception {
        //新建贷款参数bean
        LoanParamBean loanParamBean = new LoanParamBean();

        //2.校验幂等性
        logger.debug("1.幂等性校验，seqNo：{}", loanConfirmReq.getSeqNo());
        String tenantId = loanConfirmReq.getTenantId();
        String customerId = customer.getSecondLevel().getCustomerId();
        AmLoanMainInfo amLoanMainInfo = extAmLoanMainInfoMapper.selectBySeqNo(loanConfirmReq.getSeqNo(), customerId, tenantId);
        if (null != amLoanMainInfo) {
            AmLoanAccountInfo amLoanAccountInfo = extAmLoanAccountInfoMapper.selectByLoanId(amLoanMainInfo.getLoanId(), customerId, tenantId);
            List<AmRepaymentPlanInfo> planInfoList = extAmRepaymentPlanInfoMapper.selectByLoanIdAndStatus(amLoanMainInfo.getLoanId()
                    , Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_1, TransBizConstant.LOAN_PLAN_STATUS_2, TransBizConstant.LOAN_PLAN_STATUS_0)
                    , customerId, tenantId);
            LoanConfirmRes res = loanService.buildRes(loanConfirmReq.getUserId(), amLoanMainInfo, planInfoList, amLoanAccountInfo.getAccountStatus());
            return res;
        }

        //封锁码校验
        //信用就医产品跳过封锁码校验
        if (BizTypeProducts.PRODUCT_XYJY.equals(loanConfirmReq.getProductId())) {
            if (StringUtils.isBlank(loanConfirmReq.getFrozeNo()) || StringUtils.isBlank(loanConfirmReq.getSerialNo())){
                logger.warn("信用就医产品，seqNo:{},贷款确认时额度冻结流水号frozeNo、支付流水号serialNo都不能为空",loanConfirmReq.getSeqNo());
            }
        } else {
            this.validateBlockCode(customer.getFirstLevel().getBlockCode(), tenantId);
        }

        //验证合同号是否重复
        String contractNumber = loanConfirmReq.getContractNumber();
        if (StringUtils.isNotBlank(contractNumber)) {
            AmLoanMainInfo loan = extAmLoanMainInfoMapper.selectAmLoanMainInfoByLoanIdOrContractNumber(null, contractNumber, customerId, tenantId, loanConfirmReq.getChannel());
            if (null != loan) {
                logger.error(TxnRespCode.CONTRACTNUMBER_IS_EXIST.getMsg());
                throw new TxnBizException(TxnRespCode.CONTRACTNUMBER_IS_EXIST.getCode(), TxnRespCode.CONTRACTNUMBER_IS_EXIST.getUserTips());
            }
        }

        loanParamBean.setCustomer(customer);
        //3.查询渠道信息判断渠道是否存在
        logger.debug("3.查询渠道信息判断渠道是否存在,渠道：{}", loanConfirmReq.getChannel());
        PrChannelTable channel =
                channelTableService.getChannelTableByChannelIdAndStatus(TransBizConstant.YES_FLAG,
                        loanConfirmReq.getChannel(), tenantId);
        loanParamBean.setChannel(channel);
        //3.检查产品ID 若产品ID不存在 使用产品规则匹配 获取产品
        logger.debug("4.检查产品ID 若产品ID不存在 使用产品规则匹配 获取产品");
        if (StringUtils.isBlank(loanConfirmReq.getProductId())) {
            if (StringUtils.isBlank(loanConfirmReq.getProductType())) {
                logger.error(TxnRespCode.PRODUCT_TYPE_NOT_EXIST.getMsg());
                throw new TxnBizException(TxnRespCode.PRODUCT_TYPE_NOT_EXIST.getCode(), TxnRespCode.PRODUCT_TYPE_NOT_EXIST.getUserTips());
            }

            //产品规则
            logger.debug("产品ID不存在  根据规则获取产品号");
            loanConfirmReq.setProductId(getProductIdByRule(channel.getOrganizationId(), channel.getChannelId(), loanConfirmReq));

        }
        //4.根据产品Id获取产品信息
        logger.debug("5.根据产品Id获取产品信息,产品号：{}", loanConfirmReq.getProductId());
        PrProductTable product = productTableService.findProByProIdAndOrgId(loanConfirmReq.getProductId(),
                channel.getOrganizationId(), tenantId);
        loanParamBean.setProduct(product);

        // 修改客户客群
        List<CmCustomerExtInfo> cmCustomerExtInfos = extCmCustomerExtInfoMapper.queryCustomerExtInfos(customerId, tenantId, BizTypeProducts.getSymbolProductId(product.getProductId()));
        if (CollectionUtils.isNotEmpty(cmCustomerExtInfos)) {
            customer.getSecondLevel().setCustomerGroup(cmCustomerExtInfos.get(0).getCustomerGroup());
        }

        logger.debug("校验还款方式，期数");
        validaAmortMethodAndTenor(product, loanConfirmReq, loanParamBean);

        //5.校验产品相关信息
        logger.debug("6.校验产品相关信息");
        validateProduct(product, loanConfirmReq);

        //修改用户客群获取方式
        CmCustomerExtInfo info = new CmCustomerExtInfo();
        info.setChannel(channel.getChannelId());
        info.setTenantId(tenantId);
        info.setCustomerId(customerId);
        info.setProductId(product.getProductId());
        CmCustomerExtInfo res = extCmCustomerExtInfoMapper.selectByConditions(info);
        if (Objects.nonNull(res)) {
            loanParamBean.getCustomer().getSecondLevel().setCustomerGroup(res.getCustomerGroup());
        }

        //受托支付处理
        if (StringUtils.isBlank(loanConfirmReq.getEntrustedPayment())) {
            if (TransBizConstant.REMIT_WAY_3.equals(product.getRemitWay())) {
                loanConfirmReq.setEntrustedPayment(TransBizConstant.YES_FLAG);
            } else {
                loanConfirmReq.setEntrustedPayment(TransBizConstant.NO_FLAG);
            }
        }

        //校验受托支付
        if (TransBizConstant.YES_FLAG.equals(loanConfirmReq.getEntrustedPayment())) {
            this.validateEntrustedPayment(loanConfirmReq);
        }

        //6.获取放款卡信息 20200515 放款配置改造：只有下单即放款的产品才需要校验卡号
        if (TransBizConstant.YES_FLAG.equals(product.getRemitWhenLoan())) {
            //校验放款路径 放款账户
            validateRemitWayAndAccount(product);

            //根据产品配置获取放款卡
            CmCustomerBankcardInfo customerBank = getBankcardInfo(customer, loanConfirmReq, product);
            loanParamBean.setCustomerBank(customerBank);
        }

        //7.获取机构业务日期
        logger.debug("12.获取机构信息（业务日期）");
        PrOrganizationTable org = organizationTableService.findByOrId(channel.getOrganizationId(), tenantId);
        loanParamBean.setOrg(org);

        //校验放款日期
        validateLoanDate(loanConfirmReq.getLoanDate(), org.getNextProcessingDate());

        //若上传商户id 则校验商户是否存在
        verifyMerchant(tenantId, loanConfirmReq.getMerchantId());

        //8.获取利率 罚息等数据ID（规则引擎）
        logger.debug("11.获取利率 罚息等数据ID（规则引擎）");
        getInterestAndOtherTableId(loanConfirmReq, loanParamBean);
        //9.还款日生成（规则引擎）
        logger.debug("10.还款日生成（规则引擎）");
        getPaymentDay(loanParamBean, loanConfirmReq, TransBizConstant.ONE);

        //10.账单日生成（规则引擎）
        logger.debug("10.账单日生成（规则引擎）");
        getStatementDay(loanParamBean, loanConfirmReq, TransBizConstant.ONE);

        //11.查询客户额度
        logger.debug("11.查询,校验，更新客户额度");
        verifyAndModifyCustomerLimit(loanConfirmReq, customer.getSecondLevel().getCustomerId(),
                channel.getOrganizationId(), product.getProductId(), org.getNextProcessingDate());

        //12.查询合作方额度信息
        logger.debug("12.查询,校验，更新合作方额度");
        verifyAndModifyPantnerLimit(loanConfirmReq, product);
        //12-2 信用就医冻结额度占用
        if (BizTypeProducts.PRODUCT_XYJY.equals(loanConfirmReq.getProductId())) {
            //扣减额度
            if (StringUtils.isNotBlank(loanConfirmReq.getFrozeNo()) && StringUtils.isNotBlank(loanConfirmReq.getSerialNo())) {
                CustomerUseLimitFrozeFrozeReq customerUseLimitFrozeReq = getCustomerUseLimitFrozeFrozeReq(loanConfirmReq);
                iCmCustomerLimitFrozeService.creditMedicalUseFrozeLimit(customerUseLimitFrozeReq);
            }
        }
        // 13.消费兑换权益 - 每月消费达标满 99 499 999送权益
        // sendCouponActivityMonthly(loanConfirmReq);

        //13.生成订单相关信息 贷款订单+账户信息+还款计划+流水
        logger.debug("13.生成订单相关信息 贷款订单+账户信息+还款计划+流水");
        return loanService.generatingOrderRelationInfo(loanParamBean, loanConfirmReq);
    }

    /**
     * 构建额度占用的参数
     * @param loanConfirmReq
     * @return
     */
    private CustomerUseLimitFrozeFrozeReq getCustomerUseLimitFrozeFrozeReq(@NotNull LoanConfirmReq loanConfirmReq) {
        CustomerUseLimitFrozeFrozeReq customerUseLimitFrozeReq = new CustomerUseLimitFrozeFrozeReq();
        customerUseLimitFrozeReq.setFrozeNo(loanConfirmReq.getFrozeNo());
        customerUseLimitFrozeReq.setChannel(loanConfirmReq.getChannel());
        customerUseLimitFrozeReq.setUserId(loanConfirmReq.getUserId());
        customerUseLimitFrozeReq.setMerchantNo(loanConfirmReq.getMerchantId());
        customerUseLimitFrozeReq.setUseAmount(loanConfirmReq.getLoanAmount());
        customerUseLimitFrozeReq.setSeqNo(loanConfirmReq.getSeqNo());
        customerUseLimitFrozeReq.setTenantId(loanConfirmReq.getTenantId());
        customerUseLimitFrozeReq.setProductId(loanConfirmReq.getProductId());
        customerUseLimitFrozeReq.setSerialNo(loanConfirmReq.getSerialNo());
        customerUseLimitFrozeReq.setUseResumeFlag(Constant.USE_RESUME_FLAG_U);
        return customerUseLimitFrozeReq;
    }

    /**
     * 消费兑换权益 - 每月消费达标满 99 499 999送权益
     *
     * @param loanConfirmReq
     * @throws TxnException
     */
    private void sendCouponActivityMonthly(LoanConfirmReq loanConfirmReq) {

        CouponReq couponReq = new CouponReq();
        couponReq.setTenantId(loanConfirmReq.getTenantId());
        couponReq.setChannel(loanConfirmReq.getChannel());
        couponReq.setUserId(loanConfirmReq.getUserId());
        couponReq.setProductCode(loanConfirmReq.getProductId());
        couponReq.setType(CouponConstant.COUPON_TYPE_QUALIFICATION);

        try {
            // 满99 送券
            //couponReq.setActivityId(couponConfig.getSpendAtLeast99YuanMonthlyActivityId());
            if (logger.isInfoEnabled()) {
                logger.info("消费兑换权益99 请求参数：{}", JSON.toJSONString(couponReq));
            }
            activityLoanService.sendCouponByLoanStatMonthly(couponReq, new BigDecimal("99"));
        } catch (TxnException txnException) {
            logger.error("消费兑换权益 txnException:", txnException);
        } catch (Exception e) {
            logger.error("消费兑换权益 Exception:", e);
        }

        try {
            // 满499 送券
            //couponReq.setActivityId(couponConfig.getSpendAtLeast499YuanMonthlyActivityId());
            if (logger.isInfoEnabled()) {
                logger.info("消费兑换权益499 请求参数：{}", JSON.toJSONString(couponReq));
            }
            activityLoanService.sendCouponByLoanStatMonthly(couponReq, new BigDecimal("499"));
        } catch (TxnException txnException) {
            logger.error("消费兑换权益 txnException:", txnException);
        } catch (Exception e) {
            logger.error("消费兑换权益 Exception:", e);
        }

        try {
            // 满999 送券
            //couponReq.setActivityId(couponConfig.getSpendAtLeast999YuanMonthlyActivityId());
            if (logger.isInfoEnabled()) {
                logger.info("消费兑换权益999 请求参数：{}", JSON.toJSONString(couponReq));
            }
            activityLoanService.sendCouponByLoanStatMonthly(couponReq, new BigDecimal("999"));
        } catch (TxnException txnException) {
            logger.error("消费兑换权益 txnException:", txnException);
        } catch (Exception e) {
            logger.error("消费兑换权益 Exception:", e);
        }

    }

    @Override
    public LoanConfirmRes loanTrailHandle(LoanReq loanReq) throws Exception {

        //新建贷款参数bean
        LoanParamBean loanParamBean = new LoanParamBean();

        //若上传商户id 则校验商户是否存在
        verifyMerchant(loanReq.getTenantId(), loanReq.getMerchantId());
        //查询客户二级档，一级档
        logger.debug("1.查询客户信息，判断是否存在,用户号：{}", loanReq.getUserId());
        CustomerRes customer =
                customerService.getCustomerByChannelAndUserId(loanReq.getChannel(), loanReq.getUserId(),
                        loanReq.getTenantId());
        loanParamBean.setCustomer(customer);
        //查询渠道配置
        logger.debug("2.查询渠道信息判断是否存在,渠道号：{}", loanReq.getChannel());
        PrChannelTable channel = channelTableService.getChannelTableByChannelIdAndStatus(TransBizConstant.YES_FLAG,
                loanReq.getChannel(), loanReq.getTenantId());
        loanParamBean.setChannel(channel);
        //判断productId是否为空，为空则匹配产品规则，获取productId
        logger.debug("判断产品ID是否存在，否则使用产品规则 获取产品ID");
        if (StringUtils.isBlank(loanReq.getProductId())) {
            if (StringUtils.isBlank(loanReq.getProductType())) {
                logger.error(TxnRespCode.PRODUCT_TYPE_NOT_EXIST.getMsg());
                throw new TxnBizException(TxnRespCode.PRODUCT_TYPE_NOT_EXIST.getCode(), TxnRespCode.PRODUCT_TYPE_NOT_EXIST.getUserTips());
            }

            logger.debug("产品ID不存在  根据产品规则获取产品ID");
            loanReq.setProductId(getProductIdByRule(channel.getOrganizationId(), channel.getChannelId(), loanReq));
        }
        //根据productId查询产品表
        logger.debug("根据产品ID获取产品信息,产品号：{}", loanReq.getProductId());
        PrProductTable product = productTableService.findProByProIdAndOrgId(loanReq.getProductId(),
                channel.getOrganizationId(), loanReq.getTenantId());
        loanParamBean.setProduct(product);
        // 修改客户客群
        List<CmCustomerExtInfo> cmCustomerExtInfos = extCmCustomerExtInfoMapper.queryCustomerExtInfos(customer.getSecondLevel().getCustomerId(), loanReq.getTenantId(), BizTypeProducts.getSymbolProductId(product.getProductId()));
        if (CollectionUtils.isNotEmpty(cmCustomerExtInfos)) {
            customer.getSecondLevel().setCustomerGroup(cmCustomerExtInfos.get(0).getCustomerGroup());
        }
        logger.debug("校验还款方式，期数");
        validaAmortMethodAndTenor(product, loanReq, loanParamBean);

        //根据产品配置，借款金额（大小）
        logger.debug("校验产品配置（借款金额）");
        validateProduct(product, loanReq);
        loanParamBean.setProduct(product);

        //获取机构信息
        logger.debug("获取机构信息（业务日期）");
        PrOrganizationTable org = organizationTableService.findByOrId(channel.getOrganizationId(), loanReq.getTenantId());
        loanParamBean.setOrg(org);

        //校验放款日期
        validateLoanDate(loanReq.getLoanDate(), org.getNextProcessingDate());

        //获取利率 罚息等数据ID（规则引擎）
        logger.debug("获取利率 罚息等数据ID（规则引擎）,并校验还款方式是否支持");
        getInterestAndOtherTableId(loanReq, loanParamBean);

        //还款日生成（规则引擎）
        logger.debug("还款日生成（规则引擎）");
        getPaymentDay(loanParamBean, loanReq, TransBizConstant.ZERO);

        //账单日生成（规则引擎）
        logger.debug("10.账单日生成（规则引擎）");
        getStatementDay(loanParamBean, loanReq, TransBizConstant.ZERO);

        //14.账单分期处理
        verifyFirstPaymentDate(loanParamBean,loanReq);

        //生成订单，还款计划
        logger.debug("生成订单，还款计划");
        return loanService.getLoanInfoAndPaymentPlanList(loanReq, loanParamBean);
    }


    @Override
    public LoanDetailQueryRes loanDetailHandle(LoanQueryReq loanQueryReq) throws TxnException {
        LoanDetailQueryRes loanDetailQueryRes = new LoanDetailQueryRes();
        //各输入字段的合法性校验
        //客户信息校验
        String loanId = loanQueryReq.getLoanId();
        String tenantId = loanQueryReq.getTenantId();
        CmCustomerSecondLevel customer = customerService.getSecondLevelByCondition(loanQueryReq.getUserId(), loanQueryReq.getChannel(), tenantId, null);
        String customerId = customer.getCustomerId();
        // 客户日终账务处理
        custBatchService.dealCustBatch(customerId, customer.getOrganizationId(), customer.getTenantId());
        //订单信息校验
        AmLoanMainInfo loanMainInfo = loanMainInfoService.getAmLoanMainInfoByLoanIdAndCusId(loanId, customerId, tenantId);
        AmLoanAccountInfo loanAccountInfo = loanAccountMainInfoService.getAmLoanAccountInfoByLoanIdAndCusId(loanId, customerId, tenantId);

        loanService.checkLoanStatus(loanAccountInfo);
        //添加商户信息
        if (StringUtils.isNotBlank(loanMainInfo.getMerchantId())) {
            PmMerchantTable pmMerchantTable = extPmMerchantTableMapper.selectByTenantIdAndMerchantId(tenantId, loanMainInfo.getMerchantId());
            if (null != pmMerchantTable) {
                loanDetailQueryRes.setMerchantId(pmMerchantTable.getMerchantId());
                loanDetailQueryRes.setMerchantName(pmMerchantTable.getMerchantName());
            }
        }
        loanDetailQueryRes.setPayoffDate(loanAccountInfo.getPayoffDate());
        loanDetailQueryRes.setPostingDate(loanMainInfo.getPostingDate());
        loanDetailQueryRes.setDescription(loanMainInfo.getMemoid());
        loanDetailQueryRes.setUserId(loanQueryReq.getUserId());
        loanDetailQueryRes.setLoanId(loanId);
        loanDetailQueryRes.setSeqNo(loanQueryReq.getSeqNo());
        loanDetailQueryRes.setLoanAmount(loanMainInfo.getLoanAmount());
        loanDetailQueryRes.setLoanUse(loanMainInfo.getLoanUse());
        loanDetailQueryRes.setUsingAmount(loanAccountInfo.getRemainPrincipal());
        loanDetailQueryRes.setAccountStatus(loanAccountInfo.getAccountStatus());
        //根据产品ID查询产品表的贷款期数单位
        PrProductTable productTable = productTableService.findProByProIdAndOrgId(loanMainInfo.getProductId(), customer.getOrganizationId(), tenantId);
        loanDetailQueryRes.setLoanTenorUnit(productTable.getLoanDurationUnit());//产品表贷款期数单位
        loanDetailQueryRes.setLoanTenor(loanMainInfo.getTotalTenor());
        loanDetailQueryRes.setCurrentTenor(loanAccountInfo.getCastTenor());
        loanDetailQueryRes.setLoanBeginDate(loanMainInfo.getPostingDate());
        loanDetailQueryRes.setLoanEndDate(loanMainInfo.getLastPaymentDate());
        loanDetailQueryRes.setLoanDayPayFlag(productTable.getLoanDayPayFlag());
        loanDetailQueryRes.setPaymentDateDd(loanMainInfo.getPaymentDateDd());
        //查询机构信息
        PrOrganizationTable organizationTable = organizationTableService.findByOrId(customer.getOrganizationId(), customer.getTenantId());
        loanDetailQueryRes.setBusinessDate(organizationTable.getNextProcessingDate());//业务日期
        //根据还款方式参数表ID查询还款方式信息
        PrAmortMethod amortMethod = amortMethodService.findByChargerOptionAndId(loanMainInfo.getAmortMethodTableId(), loanMainInfo.getOrganizationId(), tenantId);
        loanDetailQueryRes.setAmortMethodCode(amortMethod.getChargeOption());//还款方式
        loanDetailQueryRes.setAmortMethodDesc(amortMethod.getDescription());//还款方式描述
        //本金
        BigDecimal principal = loanService.getPaidAmountByLoanIdAndStatusAndType(loanId, new String[]{TransBizConstant.ACCOUNT_TYPE_06}, customerId, tenantId);
        //利息
        BigDecimal interest = loanService.getPaidAmountByLoanIdAndStatusAndType(loanId, new String[]{TransBizConstant.ACCOUNT_TYPE_04}, customerId, tenantId);
        //罚息/费
        BigDecimal penalty = loanService.getPaidAmountByLoanIdAndStatusAndType(loanId, new String[]{TransBizConstant.ACCOUNT_TYPE_11}, customerId, tenantId);
        //复利
        BigDecimal paidCompoundInterest = loanService.getPaidAmountByLoanIdAndStatusAndType(loanId, new String[]{TransBizConstant.ACCOUNT_TYPE_12}, customerId, tenantId);
        //费用
        BigDecimal fee = loanService.getPaidAmountByLoanIdAndStatusAndType(loanId, new String[]{TransBizConstant.ACCOUNT_TYPE_03}, customerId, tenantId);

        loanDetailQueryRes.setPaidFeeAmount(fee == null ? BigDecimal.ZERO : fee);
        loanDetailQueryRes.setPaidInterestAmount(interest == null ? BigDecimal.ZERO : interest);
        loanDetailQueryRes.setPaidPenaltyAmount(penalty == null ? BigDecimal.ZERO : penalty);
        loanDetailQueryRes.setPaidCompoundInterest(paidCompoundInterest == null ? BigDecimal.ZERO : paidCompoundInterest);
        loanDetailQueryRes.setPaidPrincipalAmount(principal == null ? BigDecimal.ZERO : principal);
        //转分期显示调整，只显示实际还款金额，
        if (TransBizConstant.ACCOUNT_STATUS_6.equals(loanAccountInfo.getAccountStatus())) {
            BigDecimal principalAmount = BigDecimal.ZERO;
            BigDecimal interestAmount = BigDecimal.ZERO;
            BigDecimal feeAmount = BigDecimal.ZERO;

            //查询订单是否有还款流水
            TlPaymentLog tlPaymentLog = extTlPaymentLogMapper.selectPaymentLogByLoanIdAndStatus(
                    loanAccountInfo.getLoanId(), TransBizConstant.PAYMENT_LOG_SUM_STATUS_S, loanAccountInfo.getCustomerId(),
                    TransBizConstant.REPAYMENT_TYPE_PREPAY, loanAccountInfo.getTenantId());
            if (null != tlPaymentLog) {
                TlPaymentAllocationLog tlPaymentAllocationLog = new TlPaymentAllocationLog();
                tlPaymentAllocationLog.setCustomerId(loanAccountInfo.getCustomerId());
                tlPaymentAllocationLog.setTenantId(loanAccountInfo.getTenantId());
                tlPaymentAllocationLog.setSeqNo(tlPaymentLog.getSeqNo());
                tlPaymentAllocationLog.setAccountType(TransBizConstant.PAYMENT_ALLOCATION_TYPE_09);
                //根据09交易账户查询还款分配表
                List<TlPaymentAllocationLog> tlPaymentAllocationLogList = extTlPaymentAllocationLogMapper.selectListByCondition(tlPaymentAllocationLog);
                for (TlPaymentAllocationLog log : tlPaymentAllocationLogList) {
                    if (TransBizConstant.TXN_CODE_INST_PYMT_FEE.equals(log.getTxnCode())) {
                        feeAmount = feeAmount.add(log.getPostingAmount());
                    } else if (TransBizConstant.TXN_CODE_INST_PYMT_INTR.equals(log.getTxnCode())) {
                        interestAmount = interestAmount.add(log.getPostingAmount());
                    } else if (TransBizConstant.TXN_CODE_INST_PYMT.equals(log.getTxnCode())) {
                        principalAmount = principalAmount.add(log.getPostingAmount());
                    }
                }
                loanDetailQueryRes.setPaidFeeAmount(feeAmount);
                loanDetailQueryRes.setPaidInterestAmount(interestAmount);
                loanDetailQueryRes.setPaidPrincipalAmount(principalAmount);

                loanDetailQueryRes.setPaidPenaltyAmount(BigDecimal.ZERO);
                loanDetailQueryRes.setPaidCompoundInterest(BigDecimal.ZERO);
            } else {
                loanDetailQueryRes.setPaidFeeAmount(feeAmount);
                loanDetailQueryRes.setPaidInterestAmount(interestAmount);
                loanDetailQueryRes.setPaidPrincipalAmount(principalAmount);

                loanDetailQueryRes.setPaidPenaltyAmount(BigDecimal.ZERO);
                loanDetailQueryRes.setPaidCompoundInterest(BigDecimal.ZERO);
            }
        }
        /*权益修改 日利率修改为取贷款信息表(AM_Loan_Main_Info)的原始利率orig_daily_rate*/
        loanDetailQueryRes.setDailyRate(loanMainInfo.getOrigDailyRate());//日利率
        loanDetailQueryRes.setYearRate(loanMainInfo.getInterestRate());//年利率
        //利息参数表信息
        InterestTableRes interestTableRes = interestTableService.findByTenIdAndInterestId(tenantId, loanMainInfo.getInterestTableId());
        loanDetailQueryRes.setYearBase(interestTableService.getYearBase(interestTableRes.getYearBase()));//年利率基准因子
        PrOrganizationTable organization =
                organizationTableService.findByOrId(customer.getOrganizationId(), tenantId);
        Date bussinessDate = organization.getNextProcessingDate();
        loanDetailQueryRes.setMonthBase(interestTableService.getMonthBase(interestTableRes.getMonthBase(), bussinessDate));//月利率基准因子
        loanDetailQueryRes.setLoanPrimeRate(loanMainInfo.getLoanPrimeRate());//LPR利率
        //罚息参数表信息
        PrPenaltyRateTable penaltyRateTable = prPenaltyRateTableService.findByPenaltyId(loanMainInfo.getPenaltyTableId(), organizationTable.getOrganizationId(), tenantId);
        loanDetailQueryRes.setPenaltyDailyRate(penaltyRateTableService.getPenaltyDailyRate(loanMainInfo.getOrigDailyRate(), penaltyRateTable));//罚息日利率
        loanDetailQueryRes.setPenaltyIncreaseNum(penaltyRateTable.getIncreaseNum());//罚息利率上浮数值
        loanDetailQueryRes.setPenaltyIncreaseType(penaltyRateTable.getIncreaseType());//罚息利率上浮方式
        loanDetailQueryRes.setContractId(loanMainInfo.getContractId());//合同号
        loanDetailQueryRes.setContractNumber(loanMainInfo.getContractNumber());//外部合同号
        BigDecimal penaltyYearRate = prPenaltyRateTableService.getPenaltyDailyRate(loanMainInfo.getInterestRate(), penaltyRateTable);
        loanDetailQueryRes.setPenaltyYearRate(penaltyYearRate);

        //订单总欠款
        DebtBean debtBean = debtCalculationService.getBalance(loanAccountInfo,
                organizationTable.getNextProcessingDate(), null, null, null, false);
        loanDetailQueryRes.setTotalDebtAmount(debtBean.getTotalAmount());
        String txnTime = DateUtils.format(loanMainInfo.getTxnTime(), "yyyy-MM-dd HH:mm:ss");
        loanDetailQueryRes.setTxnTime(txnTime);
        /*if (!DateUtils.sameDate(DateUtils.parseDate(loanMainInfo.getTxnTime()).getTime(), DateUtils.parseDate(loanMainInfo.getCreateTime()).getTime())) {
            // 23.30 切日后，前端显示实际发生的时间
            loanDetailQueryRes.setPostingDate(DateUtils.addDay(loanMainInfo.getPostingDate(), -1));
            loanDetailQueryRes.setLoanBeginDate(DateUtils.addDay(loanMainInfo.getPostingDate(), -1));
            loanDetailQueryRes.setLoanEndDate(DateUtils.addDay(loanMainInfo.getLastPaymentDate(), -1));
            loanDetailQueryRes.setTxnTime(DateUtils.format((DateUtils.addDay(loanMainInfo.getTxnTime(), -1)), "yyyy-MM-dd HH:mm:ss"));
        }*/
        //查询客户权益表
        CmCustomerRightInfo cmCustomerRightInfo = customerRightService.getByCustomerIdAndTenantIdAndLoanId(
                customerId, customer.getTenantId(), loanId);
        if (null != cmCustomerRightInfo) {
            loanDetailQueryRes.setCustRightId(cmCustomerRightInfo.getId());
            loanDetailQueryRes.setRightsName(cmCustomerRightInfo.getRightsName());
        }
        // 查询放款状态
        TlDisbursementLog log = extTlDisbursementLogMapper.selectDisbursementByCondition(loanId, customerId, tenantId);
        if (Objects.nonNull(log)) {
            loanDetailQueryRes.setDisbursementStatus(log.getSumStatus());
        }
        List<RepaymentPlanInfoRes> planInfoRes = loanService.getRepaymentPlanListByLoanId(loanId, customerId, tenantId);
        BigDecimal delqAmnt = planInfoRes.stream()
                .filter(plan -> plan.getPlanStatus().equalsIgnoreCase(TransBizConstant.LOAN_PLAN_STATUS_2))
                .map(plan -> plan.getPrincipalAmount().subtract(plan.getRepayPrincipal())).reduce(BigDecimal.ZERO, BigDecimal::add).setScale(2, BigDecimal.ROUND_HALF_UP);
        long payedTenor = planInfoRes.stream().filter(plan -> Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_7, TransBizConstant.LOAN_PLAN_STATUS_8).contains(plan.getPlanStatus())).count();
        loanDetailQueryRes.setPayedTenor(payedTenor + "");
        // 逾期金额
        loanDetailQueryRes.setDelqAmnt(delqAmnt);
        return loanDetailQueryRes;
    }

    @Override
    public LoanRecordQueryRes loanRecordHandle(CommonReq commonReq) throws TxnException {
        LoanRecordQueryRes loanRecordQueryRes = new LoanRecordQueryRes();
        loanRecordQueryRes.setSeqNo(commonReq.getSeqNo());
        loanRecordQueryRes.setUserId(commonReq.getUserId());
        //客户信息校验
        CmCustomerSecondLevel customer = customerService.getSecondLevelByCondition(commonReq.getUserId(),
                commonReq.getChannel(), commonReq.getTenantId(), null);
        //查询机构信息
        PrOrganizationTable organizationTable = organizationTableService.findByOrId(customer.getOrganizationId(), customer.getTenantId());
        loanRecordQueryRes.setBusinessDate(organizationTable.getNextProcessingDate());//业务日期
        //贷款订单表，账户信息表根据渠道号和客户ID，账户类型查询贷款记录响应信息
        List<LoanRecordRes> loanRecordResList = loanService.getLoanMainByCondition(commonReq.getChannel(), customer.getCustomerId(), commonReq.getTenantId());
        for (LoanRecordRes record : loanRecordResList) {
            //根据还款方式参数表ID查询还款方式信息
            PrAmortMethod amortMethod = amortMethodService.findByChargerOptionAndId(record.getAmortMethodTableId(), customer.getOrganizationId(), commonReq.getTenantId());
            record.setAmortMethodCode(amortMethod.getChargeOption());
            record.setAmortMethodDesc(amortMethod.getDescription());
        }
        loanRecordQueryRes.setLoanRecordList(loanRecordResList);

        return loanRecordQueryRes;
    }

    @Override
    public DisbursementStatusQueryRes disbursementStatusHandle(LoanQueryReq loanQueryReq) throws TxnBizException {
        DisbursementStatusQueryRes disbursementStatusQueryRes = new DisbursementStatusQueryRes();
        disbursementStatusQueryRes.setSeqNo(loanQueryReq.getSeqNo());
        disbursementStatusQueryRes.setLoanId(loanQueryReq.getLoanId());
        disbursementStatusQueryRes.setUserId(loanQueryReq.getUserId());
        //客户信息校验
        CmCustomerSecondLevel customer = customerService.getSecondLevelByCondition(loanQueryReq.getUserId(), loanQueryReq.getChannel(),
                loanQueryReq.getTenantId(), null);
        //订单信息校验
        AmLoanMainInfo amLoanMainInfo = loanMainInfoService.getAmLoanMainInfoByLoanIdAndCusId(loanQueryReq.getLoanId(), customer.getCustomerId(), loanQueryReq.getTenantId());
        //产品查询
        PrProductTable prProductTable = productTableService.findProByProIdAndOrgId(amLoanMainInfo.getProductId(), amLoanMainInfo.getOrganizationId(), amLoanMainInfo.getTenantId());
        if (null == prProductTable) {
            logger.error("产品不存在，产品号：  " + amLoanMainInfo.getProductId());
            throw new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
        }

        if (TransBizConstant.NO_FLAG.equals(prProductTable.getRemitWhenLoan())) {
            //非下单即放款没有放款流水直接返回成功
            disbursementStatusQueryRes.setDisbursementStatus(TransBizConstant.DISBURSEMENT_STATUS_SUCCESS);
            return disbursementStatusQueryRes;
        }

        //放款状态查询
        DisbursementLogRes tlDisbursementLog = loanService.getDisbursementByCondition(loanQueryReq.getLoanId(), customer.getCustomerId(), loanQueryReq.getTenantId());
        if (TransBizConstant.PAYMENT_LOG_SUM_STATUS_S.equals(tlDisbursementLog.getSumStatus())) {
            // 放款成功日期
            String loanDate = DateFormatUtils.format(tlDisbursementLog.getCompleteTime(), DateUtils.DATE_PATTERN);
            disbursementStatusQueryRes.setLoanDate(loanDate);
            disbursementStatusQueryRes.setDisbursementStatus(TransBizConstant.DISBURSEMENT_STATUS_SUCCESS);
        } else if (TransBizConstant.PAYMENT_LOG_SUM_STATUS_F.equals(tlDisbursementLog.getSumStatus())) {
            disbursementStatusQueryRes.setDisbursementStatus(TransBizConstant.DISBURSEMENT_STATUS_ERROR);
        } else {
            disbursementStatusQueryRes.setDisbursementStatus(TransBizConstant.DISBURSEMENT_STATUS_UNKNOWN);
        }

        disbursementStatusQueryRes.setReturnCode(tlDisbursementLog.getReturnCode());
        disbursementStatusQueryRes.setReturnMessage(tlDisbursementLog.getReturnMessage());
        return disbursementStatusQueryRes;
    }

    @Override
    public LoanPlanQueryRes loanPlanHandle(LoanPlanQueryReq loanPlanQueryReq) throws TxnException {
        //校验订单号与外部合同号
        validateLoanPlanQueryReq(loanPlanQueryReq.getLoanId(), loanPlanQueryReq.getContractNumber());
        LoanPlanQueryRes loanPlanQueryRes = new LoanPlanQueryRes();
        loanPlanQueryRes.setSeqNo(loanPlanQueryReq.getSeqNo());
        loanPlanQueryRes.setLoanId(loanPlanQueryReq.getLoanId());
        loanPlanQueryRes.setUserId(loanPlanQueryReq.getUserId());
        //客户信息校验  生成一级和二级客户信息
        CmCustomerSecondLevel customer = customerService.getSecondLevelByCondition(loanPlanQueryReq.getUserId(), loanPlanQueryReq.getChannel(),
                loanPlanQueryReq.getTenantId(), null);

        // 客户日终账务处理
        custBatchService.dealCustBatch(customer.getCustomerId(), customer.getOrganizationId(), customer.getTenantId());

        AmLoanMainInfo loanMainInfo = loanMainInfoService.findLoanMainInfoByLoanIdOrContractNumber(loanPlanQueryReq.getLoanId(),
                loanPlanQueryReq.getContractNumber(), customer.getCustomerId(), loanPlanQueryReq.getTenantId(), loanPlanQueryReq.getChannel());
        //订单信息校验
        AmLoanAccountInfo loanAccountInfo = loanAccountMainInfoService.getAmLoanAccountInfoByLoanIdAndCusId(loanMainInfo.getLoanId(),
                customer.getCustomerId(), loanPlanQueryReq.getTenantId());

        loanService.checkLoanStatus(loanAccountInfo);

        if (TransBizConstant.LOAN_STATUS_9.equalsIgnoreCase(loanAccountInfo.getAccountStatus())) {
            logger.error(TxnRespCode.LOAN_ORDER_CANCEL.getMsg());
            throw new TxnBizException(TxnRespCode.LOAN_ORDER_CANCEL.getCode(), TxnRespCode.LOAN_ORDER_CANCEL.getUserTips());
        }
        //要查询的状态（还款计划）
        List<String> statusList = Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_0,
                TransBizConstant.LOAN_PLAN_STATUS_1, TransBizConstant.LOAN_PLAN_STATUS_2,
                TransBizConstant.LOAN_PLAN_STATUS_6, TransBizConstant.LOAN_PLAN_STATUS_7,
                TransBizConstant.LOAN_PLAN_STATUS_8);
        //获取该订单的还款计划列表
        List<AmRepaymentPlanInfo> repaymentPlanInfos = repaymentPlanService.getPlanList(loanMainInfo.getLoanId()
                , customer.getCustomerId(), loanPlanQueryReq.getTenantId(), statusList);
        //不存在还款计划
        if (CollectionUtils.isEmpty(repaymentPlanInfos)) {
            loanPlanQueryRes.setPlanList(Collections.emptyList());
            return loanPlanQueryRes;
        }
        List<PlanRes> planList = new ArrayList<>(repaymentPlanInfos.size());
        //罚息复利交易账户
        Map<Integer, List<AmAccountMainInfo>> accounttMap = null;
        //累计账户map key->期次 value 累计账户list
        Map<Integer, List<AmInterestAccrualInfo>> acctMap = null;
        //当订单状态处于正常或逾期时需要查询欠款
        if (TransBizConstant.LOAN_STATUS_1.equalsIgnoreCase(loanAccountInfo.getAccountStatus())
                || TransBizConstant.LOAN_STATUS_2.equalsIgnoreCase(loanAccountInfo.getAccountStatus())) {

            //获取罚息复利交易账户信息
            List<String> accountTypeList = Arrays.asList(TransBizConstant.ACCOUNT_TYPE_11,
                    TransBizConstant.ACCOUNT_TYPE_12);
            List<AmAccountMainInfo> accountMainInfoList =
                    extAmAccountMainInfoMapper.selectAccounts(loanAccountInfo.getLoanId(), accountTypeList,
                            loanAccountInfo.getCustomerId(), loanAccountInfo.getTenantId(),
                            Collections.singletonList(TransBizConstant.ACCOUNT_STATUS_2));
            if (CollectionUtils.isNotEmpty(accountMainInfoList)) {
                accounttMap = accountMainInfoList.stream()
                        .collect(Collectors.groupingBy(AmAccountMainInfo::getBillingTenor));
            }
            //获取累计账户信息
            List<String> typeList = Arrays.asList(TransBizConstant.INTEREST_ACCRUAL_TYPE_20,
                    TransBizConstant.INTEREST_ACCRUAL_TYPE_10);
            List<AmInterestAccrualInfo> interestAccrualInfoList =
                    extAmInterestAccrualInfoMapper.selectAllAccuAcct(loanAccountInfo.getLoanId(),
                            loanAccountInfo.getCustomerId(), loanAccountInfo.getTenantId(), typeList,
                            Collections.singletonList(TransBizConstant.ACCOUNT_STATUS_1));
            if (CollectionUtils.isNotEmpty(interestAccrualInfoList)) {
                //将累计账户按交易类型分组求欠款之和
                acctMap = interestAccrualInfoList.stream()
                        .collect(Collectors.groupingBy(AmInterestAccrualInfo::getBillingTenor));
            }
        }
        List<AmRepaymentPlanInfo> amRepaymentPlanInfoList = repaymentPlanInfos.stream().sorted(Comparator.comparingInt(AmRepaymentPlanInfo::getBillingTenor)).collect(Collectors.toList());
        for (AmRepaymentPlanInfo info : amRepaymentPlanInfoList) {
            BigDecimal penaltyInterest = BigDecimal.ZERO;
            BigDecimal compoundInterest = BigDecimal.ZERO;
            if (acctMap != null && CollectionUtils.isNotEmpty(acctMap.get(info.getBillingTenor()))) {
                Map<String, BigDecimal> bigDecimalMap = acctMap.get(info.getBillingTenor()).stream()
                        .collect(Collectors.groupingBy(AmInterestAccrualInfo::getAccountType,
                                CollectorsUtils.summingBigDecimal(AmInterestAccrualInfo::getAccrualTotalAccruInterest)));
                penaltyInterest =
                        Optional.ofNullable(bigDecimalMap.get(TransBizConstant.INTEREST_ACCRUAL_TYPE_10)).orElse(BigDecimal.ZERO);//罚息
                compoundInterest =
                        Optional.ofNullable(bigDecimalMap.get(TransBizConstant.INTEREST_ACCRUAL_TYPE_20)).orElse(BigDecimal.ZERO);//复利
            }
            if (accounttMap != null && CollectionUtils.isNotEmpty(accounttMap.get(info.getBillingTenor()))) {
                Map<String, BigDecimal> bigDecimalMap = accounttMap.get(info.getBillingTenor()).stream()
                        .collect(Collectors.groupingBy(AmAccountMainInfo::getAccountType,
                                CollectorsUtils.summingBigDecimal(AmAccountMainInfo::getCurrBalance)));

                penaltyInterest =
                        penaltyInterest.add(Optional.ofNullable(bigDecimalMap.get(TransBizConstant.ACCOUNT_TYPE_11)).orElse(BigDecimal.ZERO));
                compoundInterest =
                        compoundInterest.add(Optional.ofNullable(bigDecimalMap.get(TransBizConstant.ACCOUNT_TYPE_12)).orElse(BigDecimal.ZERO));
            }
            PlanRes planRes = new PlanRes();
            planRes.setTenorNo(info.getBillingTenor());//期数
            planRes.setPaymentDueDate(info.getPaymentDueDate());//还款日
            planRes.setPayablePrincipal(info.getPrincipalAmount());//应还本金
            planRes.setPaymentPrincipal(info.getRepayPrincipal());//实还本金
            planRes.setPayableInterest(info.getInterestAmount());//应还利息
            planRes.setPaymentInterest(info.getRepayInterest());//实还利息
            planRes.setPayablePenaltyInterest(penaltyInterest.add(info.getRepayPenaltyInterest()));//应还罚息
            planRes.setPaymentPenaltyInterest(info.getRepayPenaltyInterest());//实还罚息
            planRes.setPayableCompoundInterest(compoundInterest.add(info.getRepayCompoundInterest()));//应还复利
            planRes.setPaymentCompoundInterest(info.getRepayCompoundInterest());//实还复利
            planRes.setPayableFee(info.getFeeAmount());//应还费用
            planRes.setPaymentFee(info.getRepayFee());//实还费用
            planRes.setPenaltyIntAmount(penaltyInterest);//剩余应还罚息
            planRes.setCompoundAmount(compoundInterest);//剩余应还复利
            planRes.setPaymentFlag(info.getPlanStatus());
            planRes.setPaymentDate(info.getTransactionDate());//实际还款日
            /*if (!Objects.isNull(info.getTransactionDate()) && !DateUtils.sameDate(DateUtils.parseDate(info.getTransactionDate()).getTime() , DateUtils.parseDate(info.getUpdateTime()).getTime())) {
                // 23.30 切日后，前端显示实际发生的时间
                planRes.setPaymentDate(DateUtils.addDay(info.getTransactionDate(), -1));//实际还款日
            }*/
            planRes.setExemptAmount(info.getWaivedAmount());//优惠金额
            planRes.setPrincipalAmount(planRes.getPayablePrincipal().subtract(planRes.getPaymentPrincipal()));//剩余应还本金
            planRes.setInterestAmount(planRes.getPayableInterest().subtract(planRes.getPaymentInterest()));//剩余应还利息
            planRes.setFeeAmount(planRes.getPayableFee().subtract(planRes.getPaymentFee()));//剩余应还费用
            BigDecimal totalAmount =
                    planRes.getPrincipalAmount().add(planRes.getPenaltyIntAmount()).add(planRes.getInterestAmount()).add(planRes.getFeeAmount().add(planRes.getCompoundAmount()));
            planRes.setTotalAmount(totalAmount);//剩余应还款
            planList.add(planRes);
        }
        loanPlanQueryRes.setPlanList(planList);
        return loanPlanQueryRes;
    }

    @Override
    public LoanCollectionRes loanCollectionHandle(LoanCollectionReq loanCollectionReq) throws TxnBizException {
        LoanCollectionRes res = new LoanCollectionRes();
        //校验订单号与外部合同号
        validateLoanPlanQueryReq(loanCollectionReq.getLoanId(), loanCollectionReq.getContractNumber());
        //客户信息校验
        CmCustomerSecondLevel customer = customerService.getSecondLevelByCondition(loanCollectionReq.getUserId(), loanCollectionReq.getChannel(),
                loanCollectionReq.getTenantId(), null);
        //订单信息校验
        AmLoanMainInfo loanMainInfo = loanMainInfoService.findLoanMainInfoByLoanIdOrContractNumber(loanCollectionReq.getLoanId(),
                loanCollectionReq.getContractNumber(), customer.getCustomerId(), loanCollectionReq.getTenantId(), loanCollectionReq.getChannel());
        AmLoanAccountInfo loanAccountInfo = loanAccountMainInfoService.getAmLoanAccountInfoByLoanIdAndCusId(loanMainInfo.getLoanId(),
                customer.getCustomerId(), loanCollectionReq.getTenantId());
        if (TransBizConstant.LOAN_STATUS_9.equalsIgnoreCase(loanAccountInfo.getAccountStatus())) {
            logger.error(TxnRespCode.LOAN_ORDER_CANCEL.getMsg());
            throw new TxnBizException(TxnRespCode.LOAN_ORDER_CANCEL.getCode(), TxnRespCode.LOAN_ORDER_CANCEL.getUserTips());
        }
        if (TransBizConstant.LOAN_STATUS_7.equals(loanAccountInfo.getAccountStatus()) || TransBizConstant.LOAN_STATUS_8.equals(loanAccountInfo.getAccountStatus())) {
            logger.error(TxnRespCode.LOAN_ORDER_CLEAR.getMsg());
            throw new TxnBizException(TxnRespCode.LOAN_ORDER_CLEAR.getCode(), TxnRespCode.LOAN_ORDER_CLEAR.getUserTips());
        }
        //判断订单是否已经入催
        TlCollectionLoanList tlCollectionLoanList = extTlCollectionLoanListMapper.selectByLoanIdAndTenantId(loanMainInfo.getLoanId(), loanMainInfo.getTenantId());
        if (tlCollectionLoanList != null) {
            if (TransBizConstant.NO_FLAG.equals(loanAccountInfo.getCompelCollectionFlag())) {
                //更新贷款账户表 强制入催标识为 1
                AmLoanAccountInfo updateInfo = new AmLoanAccountInfo();
                updateInfo.setId(loanAccountInfo.getId());
                updateInfo.setCustomerId(loanAccountInfo.getCustomerId());
                updateInfo.setTenantId(loanAccountInfo.getTenantId());
                updateInfo.setCompelCollectionFlag(TransBizConstant.YES_FLAG);
                extAmLoanAccountInfoMapper.updateByPrimaryKey(updateInfo);

                res.setSeqNo(loanCollectionReq.getSeqNo());
                return res;
            }
        }
        //入库
        TlCollectionLoanList newInfo = new TlCollectionLoanList();
        try {
            newInfo.setId(segmentService.getId(TransBizConstant.TL_COLLCTION_LOAN_LIST_ID));
        } catch (SegmentException e) {
            logger.error("主键生成错误", e);
            throw new TxnBizException(ErrorMsgConstant.ERROR_ID);
        }
        newInfo.setLoanId(loanMainInfo.getLoanId());
        newInfo.setOrganizationId(loanMainInfo.getOrganizationId());
        newInfo.setTenantId(loanMainInfo.getTenantId());
        newInfo.setChannel(loanMainInfo.getChannel());
        newInfo.setProductId(loanMainInfo.getProductId());
        newInfo.setCustomerId(customer.getCustomerId());
        newInfo.setCompelCollectionFlag(TransBizConstant.YES_FLAG);
        newInfo.setUpdateBy(TransBizConstant.SYS_OPRATE);
        newInfo.setRecordVersionNumber(TransBizConstant.INIT_RECORD_VERSION_NUMBER);
        tlCollectionLoanListMapper.insertSelective(newInfo);

        //更新贷款账户表 强制入催标识为 1
        AmLoanAccountInfo updateInfo = new AmLoanAccountInfo();
        updateInfo.setId(loanAccountInfo.getId());
        updateInfo.setCustomerId(loanAccountInfo.getCustomerId());
        updateInfo.setTenantId(loanAccountInfo.getTenantId());
        updateInfo.setCompelCollectionFlag(TransBizConstant.YES_FLAG);
        extAmLoanAccountInfoMapper.updateByPrimaryKey(updateInfo);

        res.setSeqNo(loanCollectionReq.getSeqNo());

        return res;
    }

    /**
     * 产品规则获取
     *
     * @param orgId   机构ID
     * @param channel 渠道
     * @param loanReq 贷款请求
     * @return 产品id
     */
    private String getProductIdByRule(String orgId, String channel, LoanReq loanReq) throws TxnBizException {

        Map<String, Object> map = new HashMap<>();
        map.put(TransBizConstant.LOAN_RULE_TRANS_CHANNEL, channel);
        map.put(TransBizConstant.LOAN_RULE_TRANS_DATE, loanReq.getLoanDate());
        map.put(TransBizConstant.LOAN_RULE_TRANS_AMOUNT, loanReq.getLoanAmount());
        map.put(TransBizConstant.LOAN_RULE_TENOR, loanReq.getTenor());
        map.put(TransBizConstant.LOAN_RULE_AMORT_METHOD, loanReq.getAmortMethod());
        map.put(TransBizConstant.LOAN_RULE_MERCHANT_ID, loanReq.getMerchantId());
        map.put(TransBizConstant.LOAN_RULE_PRODUCT_TYPE, loanReq.getProductType());
        RuleMatchReq ruleMatchReq = new RuleMatchReq(orgId, null, TransBizConstant.PRO_MATCH_RULE_TYPE, map,
                loanReq.getTenantId());
        return aviatorService.executeRuleResultMap(ruleMatchReq).get(TransBizConstant.LOAN_RULE_RES_KEY).toString();
    }


    /**
     * 校验还款方式和期次
     *
     * @param product
     * @param loanReq
     * @param loanParamBean
     */
    private void validaAmortMethodAndTenor(PrProductTable product, LoanReq loanReq, LoanParamBean loanParamBean) throws Exception {

        Map<String, Object> map = new HashMap<>();
        map.put(TransBizConstant.LOAN_RULE_TRANS_CHANNEL, loanReq.getChannel());
        map.put(TransBizConstant.LOAN_RULE_TRANS_AMOUNT, loanReq.getLoanAmount());
        map.put(TransBizConstant.LOAN_RULE_CUS_IDENTIFY, loanParamBean.getCustomer().getSecondLevel().getCustomerGroup());
        map.put(TransBizConstant.LOAN_RULE_CUSTOMER_LEVEL, loanParamBean.getCustomer().getFirstLevel().getCustomerLevel());
        RuleMatchReq ruleMatchReq = new RuleMatchReq(product.getOrganizationId(), product.getProductId(),
                TransBizConstant.AMORT_METHOD_TABLE_ID, map, product.getTenantId());
        List<Map<String, Object>> amortMethodIdList = aviatorService.executeRuleList(ruleMatchReq);

        boolean tenorFlag = false;
        for (Map<String, Object> amortMap : amortMethodIdList) {
            List<String> amortList = (List<String>) amortMap.get(TransBizConstant.LOAN_RULE_RES_KEY);
            String terms = (String) amortMap.get(TransBizConstant.LOAN_RULE_AMORT_METHOD_KEY);
            List<String> allowTermList = Arrays.asList(terms.split(","));
            if (amortList.contains(loanReq.getAmortMethod()) && allowTermList.contains(loanReq.getTenor().toString())) {
                tenorFlag = true;
                loanParamBean.setAmortMethodTableIds(amortList);
                continue;
            }
        }

        if (!tenorFlag) {
            logger.debug(TxnRespCode.LOAN_ALLOW_TENOR.getMsg());
            throw new TxnBizException(TxnRespCode.LOAN_ALLOW_TENOR.getCode(), TxnRespCode.LOAN_ALLOW_TENOR.getUserTips());
        }
    }


    /**
     * 校验产品信息
     *
     * @param product 产品信息
     * @param loanReq 贷款请求
     */
    private void validateProduct(PrProductTable product, LoanReq loanReq) throws TxnBizException {
        //对比产品最大贷款金额
        if (loanReq.getLoanAmount().compareTo(product.getMaxAmount()) > 0) {
            logger.debug(TxnRespCode.LOAN_GT_MAX_AMOUNT.getMsg());
            throw new TxnBizException(TxnRespCode.LOAN_GT_MAX_AMOUNT.getCode(), TxnRespCode.LOAN_GT_MAX_AMOUNT.getFormatUserTips(product.getMaxAmount()));
        }
        //对比产品最小贷款金额
        if (loanReq.getLoanAmount().compareTo(product.getMinAmount()) < 0) {
            logger.debug(TxnRespCode.LOAN_LT_MIN_AMOUNT.getMsg());
            throw new TxnBizException(TxnRespCode.LOAN_LT_MIN_AMOUNT.getCode(), TxnRespCode.LOAN_LT_MIN_AMOUNT.getFormatUserTips(product.getMinAmount()));
        }
        // 如果是联合贷则资金源id必输
        if (TransBizConstant.Y_FLAG.equals(product.getAllowJointFlag()) && StringUtils.isEmpty(loanReq.getFundId())) {
            logger.debug(TxnRespCode.FUND_ID_NOT_EMPTY.getMsg());
            throw new TxnBizException(TxnRespCode.FUND_ID_NOT_EMPTY.getCode(), TxnRespCode.FUND_ID_NOT_EMPTY.getUserTips());
        }
    }

    /**
     * 获取还款日
     *
     * @param loanParamBean 贷款请求参数
     * @param loanReq       贷款请求
     * @param flag          贷款试算和确认标志  （0：试算，1：确认）
     */
    private void getPaymentDay(LoanParamBean loanParamBean, LoanReq loanReq, Integer flag) throws TxnBizException {
        PrProductTable prProductTable = loanParamBean.getProduct();
        String paymentDateType = prProductTable.getPaymentDateType();
        String loanDurationUnit = prProductTable.getLoanDurationUnit();
        Integer loanDurationValue = prProductTable.getLoanDurationValue();
        Integer statemntInterval = prProductTable.getStatemntInterval();
        Date firstPaymentDate = null;
        PrChannelTable channel = loanParamBean.getChannel();
        CustomerRes customer = loanParamBean.getCustomer();
        PrProductTable product = loanParamBean.getProduct();
        Integer paymentDay = null;
        Date loanDate = loanReq.getLoanDate() == null ? loanParamBean.getOrg().getNextProcessingDate() : loanReq.getLoanDate();
        boolean updateCustomerPaymentDayFlag = false;//是否更新客户还款日
        if (TransBizConstant.PAYMENT_DATE_TYPE_1.equals(paymentDateType)) {
            //交易入账日起息   入账日的时候再计算还款日
            if (loanParamBean.getInterestTable().getStartDateFlag().equals(TransBizConstant.START_DATE_FLAG_1)) {
                return;
            }
            //固定账单日
            if (TransBizConstant.IS_FIXED_BILLING.equals(channel.getIsFixedBilling())) {
                if (null == customer.getSecondLevel().getPaymentDd()) {
                    ////根据还款日规则获取
                    //paymentDay = loanService.getPaymentDayByRule(channel.getOrganizationId(), product.getProductId(), loanDate, loanReq.getTenantId());
                    updateCustomerPaymentDayFlag = true;
            //    } else {
            //        paymentDay = customer.getSecondLevel().getPaymentDd();
                }
            //} else {
            }
            paymentDay = loanService.getPaymentDayByRule(channel.getOrganizationId(), product.getProductId(), loanDate, loanReq.getTenantId());

            //计算首次还款日
            firstPaymentDate = repaymentPlanService.computeFirstPaymentDate(loanDate, paymentDay, statemntInterval);
        } else if (TransBizConstant.PAYMENT_DATE_TYPE_2.equals(paymentDateType)) {
            //上送还款日，已确定账单日和客户还款日
            if (null == loanReq.getPaymentDate()) {
                throw new TxnBizException(TxnRespCode.NOT_EMPTY.getCode(), TxnRespCode.NOT_EMPTY.getUserTips());
            }
            firstPaymentDate = loanReq.getPaymentDate();
            paymentDay = Integer.parseInt(DateUtils.getDay(firstPaymentDate));
        } else if (TransBizConstant.PAYMENT_DATE_TYPE_3.equals(paymentDateType)) {
            //采用账单日规则
            if (!TransBizConstant.PRIN_CHARGE_OPTION_INTPAY.equals(loanReq.getAmortMethod())) {
                logger.error(TxnRespCode.LOAN_PRODUCT_AMORT_METHOD_NOT_EXIST.getMsg());
                throw new TxnBizException(TxnRespCode.LOAN_PRODUCT_AMORT_METHOD_NOT_EXIST.getCode(),
                        TxnRespCode.LOAN_PRODUCT_AMORT_METHOD_NOT_EXIST.getUserTips());
            }
            //交易入账日起息   入账日的时候再计算还款日
            if (loanParamBean.getInterestTable().getStartDateFlag().equals(TransBizConstant.START_DATE_FLAG_1)) {
                return;
            }
            if (TransBizConstant.LOAN_DURATION_MONTH.equals(loanDurationUnit)) {//月
                firstPaymentDate = DateUtils.getDateByMonth(loanDate, loanDurationValue);
            } else if (TransBizConstant.LOAN_DURATION_WEEK.equals(loanDurationUnit)) {//周
                firstPaymentDate = DateUtils.getDate(loanDate, loanDurationValue * 7);
            } else {//天
                firstPaymentDate = DateUtils.getDate(loanDate, loanDurationValue);
            }
            paymentDay = Integer.parseInt(DateUtils.getDay(firstPaymentDate));
        }

        loanParamBean.setFirstPaymentDate(firstPaymentDate);
        loanParamBean.setPaymentDay(paymentDay);
        //试算
        if (flag.equals(TransBizConstant.ZERO)) {
            return;
        }

        //交易生效日起息  立即抛帐
        if (loanParamBean.getInterestTable().getStartDateFlag().equals(TransBizConstant.START_DATE_FLAG_0) && updateCustomerPaymentDayFlag) {
            customerService.editPaymentDd(customer.getSecondLevel().getCustomerId(),
                    channel.getOrganizationId(), channel.getChannelId(), paymentDay, loanReq.getTenantId());
        }
    }

    /**
     * 获取账单日
     *
     * @param loanParamBean 贷款请求参数
     * @param loanReq       贷款请求
     * @param flag          贷款试算和确认标志  （0：试算，1：确认）
     */
    private void getStatementDay(LoanParamBean loanParamBean, LoanReq loanReq, Integer flag) throws TxnBizException {
        PrProductTable prProductTable = loanParamBean.getProduct();
        String statementDayType = prProductTable.getStatementDayType();
        PrChannelTable channel = loanParamBean.getChannel();
        CustomerRes customer = loanParamBean.getCustomer();
        PrProductTable product = loanParamBean.getProduct();
        Integer paymentDay = loanParamBean.getPaymentDay();
        Integer statementDay = null;
        Date loanDate = loanReq.getLoanDate() == null ? loanParamBean.getOrg().getNextProcessingDate() : loanReq.getLoanDate();
        //计算首次还款日
        Date firstPaymentDate = loanParamBean.getFirstPaymentDate();
        boolean updateCustomerStatementDayFlag = false;//是否更新客户账单日
        if (TransBizConstant.STATEMENT_DATE_TYPE_3.equals(statementDayType)) {
            //交易入账日起息   入账日的时候再计算还款日
            if (loanParamBean.getInterestTable().getStartDateFlag().equals(TransBizConstant.START_DATE_FLAG_1)) {
                return;
            }
            //固定账单日，如果二级档有取二级档,否则通过规则获取
            if (TransBizConstant.IS_FIXED_BILLING.equals(channel.getIsFixedBilling())) {
                if (null == customer.getSecondLevel().getStatementDd()) {
                    //statementDay = loanService.getStatementDayByRule(channel.getOrganizationId(), product.getProductId(), loanDate, loanReq.getTenantId());
                    updateCustomerStatementDayFlag = true;
                //} else {
                //    statementDay = customer.getSecondLevel().getStatementDd();
                }
            //} else {
            }
            //非固定账单日通过规则获取
            statementDay = loanService.getStatementDayByRule(channel.getOrganizationId(), product.getProductId(), loanDate, loanReq.getTenantId());

            firstPaymentDate = loanService.getFirstPaymentDate(paymentDay, statementDay, loanDate);

        } else if (TransBizConstant.STATEMENT_DATE_TYPE_2.equals(statementDayType)) {
            //上送账单日
            if (null == loanReq.getStatementDay()) {
                throw new TxnBizException(TxnRespCode.NOT_EMPTY.getCode(), TxnRespCode.NOT_EMPTY.getUserTips());
            }
            statementDay = loanReq.getStatementDay();
            firstPaymentDate = loanService.getFirstPaymentDate(paymentDay, statementDay, loanDate);

        } else if (TransBizConstant.STATEMENT_DATE_TYPE_1.equals(statementDayType)) {
            //交易入账日起息   入账日的时候再计算还款日
            if (loanParamBean.getInterestTable().getStartDateFlag().equals(TransBizConstant.START_DATE_FLAG_1)) {
                return;
            }
            //和还款日一致
            //非固定账单日和还款日一致
            statementDay = loanParamBean.getPaymentDay();
            //固定账单日，如果二级档有取二级档,否则和还款日一致
            if (TransBizConstant.IS_FIXED_BILLING.equals(channel.getIsFixedBilling())) {
                if (null == customer.getSecondLevel().getStatementDd()) {
                    //statementDay = loanParamBean.getPaymentDay();
                    updateCustomerStatementDayFlag = true;
                } /*else {
                    statementDay = customer.getSecondLevel().getStatementDd();
                }*/
            } /*else {
                //非固定账单日和还款日一致
                statementDay = loanParamBean.getPaymentDay();
            }*/
        }

        loanReq.setStatementDay(statementDay);
        loanParamBean.setFirstPaymentDate(firstPaymentDate);
        //试算
        if (flag.equals(TransBizConstant.ZERO)) {
            return;
        }

        //交易生效日起息  立即抛帐
        if (loanParamBean.getInterestTable().getStartDateFlag().equals(TransBizConstant.START_DATE_FLAG_0) && updateCustomerStatementDayFlag) {
            customerService.editByUserIdAndOrgIdAndChannel(customer.getSecondLevel().getCustomerId(),
                    channel.getOrganizationId(), channel.getChannelId(), statementDay, loanReq.getTenantId());
        }
    }


    /**
     * 获取客户权,并校验贷款请求
     *
     * @param loanParamBean
     * @param loanReq
     */
    private void getCustRight(LoanParamBean loanParamBean, LoanReq loanReq) throws TxnBizException {
        String customerId = loanParamBean.getCustomer().getSecondLevel().getCustomerId();
        String tenantId = loanReq.getTenantId();
        Long custRightId = loanReq.getCustRightId();
        if (null == custRightId) {
            return;
        }

        CmCustomerRightInfo rightInfo = customerRightService.getRightInfoById(custRightId, customerId, tenantId);

        if (CustomerConstant.RIGHTS_STATUS_1.equals(rightInfo.getRightsStatus())) {
            logger.info(TxnRespCode.CUSTOMER_RIGHT_ALREADY_USED.getMsg());
            throw new TxnBizException(TxnRespCode.CUSTOMER_RIGHT_ALREADY_USED.getCode(), TxnRespCode.CUSTOMER_RIGHT_ALREADY_USED.getUserTips());
        }

        Date businessDate = loanParamBean.getOrg().getNextProcessingDate();
        Date effectDate = rightInfo.getEffectDate();
        Date expiresDate = rightInfo.getExpiresDate();
        if (businessDate.compareTo(effectDate) < 0 || businessDate.compareTo(expiresDate) > 0) {
            logger.info(TxnRespCode.CUSTOMER_RIGHT_DATE_NOT_RIGHT.getMsg());
            throw new TxnBizException(TxnRespCode.CUSTOMER_RIGHT_DATE_NOT_RIGHT.getCode(), TxnRespCode.CUSTOMER_RIGHT_DATE_NOT_RIGHT.getUserTips());
        }

        PrProductTable prProductTable = loanParamBean.getProduct();
        String allowProducts = rightInfo.getAllowProducts();
        if (!allowProducts.contains(prProductTable.getProductId())) {
            logger.info(TxnRespCode.CUSTOMER_RIGHT_PRODUCT_NOT_RIGHT.getMsg());
            throw new TxnBizException(TxnRespCode.CUSTOMER_RIGHT_PRODUCT_NOT_RIGHT.getCode(), TxnRespCode.CUSTOMER_RIGHT_PRODUCT_NOT_RIGHT.getUserTips());
        }

        BigDecimal minAmt = rightInfo.getMinAmount();
        if (null != minAmt && minAmt.compareTo(loanReq.getLoanAmount()) > 0) {
            logger.info(TxnRespCode.CUSTOMER_RIGHT_AMT_NOT_RIGHT.getMsg());
            throw new TxnBizException(TxnRespCode.CUSTOMER_RIGHT_AMT_NOT_RIGHT.getCode(), TxnRespCode.CUSTOMER_RIGHT_AMT_NOT_RIGHT.getUserTips());
        }

        BigDecimal maxAmt = rightInfo.getMaxAmount();
        if (null != maxAmt && maxAmt.compareTo(loanReq.getLoanAmount()) < 0) {
            logger.info(TxnRespCode.CUSTOMER_RIGHT_AMT_NOT_RIGHT.getMsg());
            throw new TxnBizException(TxnRespCode.CUSTOMER_RIGHT_AMT_NOT_RIGHT.getCode(), TxnRespCode.CUSTOMER_RIGHT_AMT_NOT_RIGHT.getUserTips());
        }

        Integer tenor = loanReq.getTenor();
        String allowLoanTenor = rightInfo.getAllowLoanTenor();
        if (StringUtils.isNotBlank(allowLoanTenor)) {
            String[] allowTenors = allowLoanTenor.split(",");
            if (Arrays.stream(allowTenors).noneMatch(s -> s.equals(tenor.toString()))) {
                logger.info(TxnRespCode.CUSTOMER_RIGHT_TENOR_NOT_RIGHT.getMsg());
                throw new TxnBizException(TxnRespCode.CUSTOMER_RIGHT_TENOR_NOT_RIGHT.getCode(), TxnRespCode.CUSTOMER_RIGHT_TENOR_NOT_RIGHT.getUserTips());
            }
        }

        loanParamBean.setRightInfo(rightInfo);
    }

    /**
     * 获取利率 罚息等数据ID（规则引擎）
     *
     * @param loanReq       贷款请求
     * @param loanParamBean 贷款请求参数
     */
    private void getInterestAndOtherTableId(LoanReq loanReq, LoanParamBean loanParamBean) throws TxnException {
        //规则匹配相关参数
        Map<String, Object> map = new HashMap<>();
        map.put(TransBizConstant.LOAN_RULE_TRANS_CHANNEL, loanReq.getChannel());//渠道
        map.put(TransBizConstant.LOAN_RULE_CUS_IDENTIFY,
                loanParamBean.getCustomer().getSecondLevel().getCustomerGroup());//客群
        map.put(TransBizConstant.LOAN_RULE_TENOR, loanReq.getTenor());//期数
        map.put(TransBizConstant.LOAN_RULE_TRANS_AMOUNT, loanReq.getLoanAmount());//借款金额
        map.put(TransBizConstant.LOAN_RULE_CUSTOMER_LEVEL, loanParamBean.getCustomer().getFirstLevel().getCustomerLevel());//客户等级
        logger.debug("获取利率表ID");
        RuleMatchReq ruleMatchReq = new RuleMatchReq(loanParamBean.getChannel().getOrganizationId(),
                loanReq.getProductId(), TransBizConstant.PRO_INTEREST_TABLE_ID, map, loanReq.getTenantId());
        String interestId =
                (String) aviatorService.executeRuleResultMap(ruleMatchReq).get(TransBizConstant.LOAN_RULE_RES_KEY);
        loanParamBean.setInterestTableId(interestId);
        //获取利率信息
        logger.debug("获取利率信息");
        PrInterestTable interestTable = interestTableService.findInterestByInterestId(interestId, loanReq.getTenantId());
        loanParamBean.setInterestTable(interestTable);

        //2.获取提前结清参数
        logger.debug("提前结清参数表ID");
        ruleMatchReq.setRuleType(TransBizConstant.PRO_PAYOFF_TABLE_ID);
        String payoffId =
                (String) aviatorService.executeRuleResultMap(ruleMatchReq).get(TransBizConstant.LOAN_RULE_RES_KEY);
        loanParamBean.setPayoffTableId(payoffId);


        //3.罚息参数表ID
        logger.debug("罚息参数表ID");
        ruleMatchReq.setRuleType(TransBizConstant.PRO_PENALTY_TABLE_ID);
        String penaltyId =
                (String) aviatorService.executeRuleResultMap(ruleMatchReq).get(TransBizConstant.LOAN_RULE_RES_KEY);
        loanParamBean.setPenaltyTableId(penaltyId);

        //4.还款顺序表ID
        logger.debug("还款顺序表ID");
        ruleMatchReq.setRuleType(TransBizConstant.PRO_PYMT_ALLOC_TABLE_ID);
        String paymentSeqId =
                (String) aviatorService.executeRuleResultMap(ruleMatchReq).get(TransBizConstant.LOAN_RULE_RES_KEY);
        loanParamBean.setPaymentSequenceTableId(paymentSeqId);


        //5.费用参数表id
        logger.debug("费用参数表id");
        ruleMatchReq.setRuleType(TransBizConstant.PRO_FEE_TABLE_ID);
        String feeId = (String) aviatorService.executeRuleResultMap(ruleMatchReq).get(TransBizConstant.LOAN_RULE_RES_KEY);
        loanParamBean.setFeeTableId(feeId);

        //6.延滞参数表id
        logger.debug("延滞参数表id");
        ruleMatchReq.setRuleType(TransBizConstant.PRO_DELQ_TABLE_ID);
        String delqId =
                (String) aviatorService.executeRuleResultMap(ruleMatchReq).get(TransBizConstant.LOAN_RULE_RES_KEY);
        loanParamBean.setDelqId(delqId);

        //7.延滞参数表id
        logger.debug("提前还款参数表id");
        ruleMatchReq.setRuleType(TransBizConstant.PRO_PRE_PAY_TABLE_ID);
        String prePayId =
                (String) aviatorService.executeRuleResultMap(ruleMatchReq).get(TransBizConstant.LOAN_RULE_RES_KEY);
        loanParamBean.setPrePayId(prePayId);

        //8.获取贴息参数表id
        PrProductTable prProductTable = loanParamBean.getProduct();
        if (TransBizConstant.INTEREST_DISCOUNT_FLAG_Y.equals(prProductTable.getInterestDiscountFlag())) {
            map.put(TransBizConstant.LOAN_RULE_MERCHANT_ID, loanReq.getMerchantId());
            map.put(TransBizConstant.LOAN_RULE_CHANNEL, loanReq.getChannel());

            logger.debug("获取贴息利率表ID");
            RuleMatchReq waiveRuleMatchReq = new RuleMatchReq(loanParamBean.getChannel().getOrganizationId(),
                    loanReq.getProductId(), TransBizConstant.PRO_WAIVE_INTEREST_TABLE_ID, map, loanReq.getTenantId());
            String waiveInterestId = (String) aviatorService.executeRuleResultMap(waiveRuleMatchReq).get(TransBizConstant.LOAN_RULE_RES_KEY);
            loanParamBean.setWaiveInterestId(waiveInterestId);
            PrWaiveInterestTable prWaiveInterestTable = prWaiveInterestTableService.findWaiveInterestByInterestId(waiveInterestId, loanReq.getTenantId());
            loanParamBean.setPrWaiveInterestTable(prWaiveInterestTable);
        }
        logger.debug("贴息参数表id");

        //9.获取客户权益
        this.getCustRight(loanParamBean, loanReq);
        logger.debug("获取客户权益");
    }


    @Override
    public LoanConfirmRes loanConfirmHandlerByLoanStaging(LoanConfirmReq loanConfirmReq) throws Exception {
        //新建贷款参数bean
        LoanParamBean loanParamBean = new LoanParamBean();

        //1.查询客户信息，判断客户信息是否存在
        logger.debug("1.查询客户信息，判断客户信息是否存在,用户号：{}", loanConfirmReq.getUserId());
        CustomerRes customer =
                customerService.getCustomerByChannelAndUserId(loanConfirmReq.getChannel(), loanConfirmReq.getUserId(),
                        loanConfirmReq.getTenantId());

        //2.校验幂等性
        logger.debug("1.幂等性校验，seqNo：{}", loanConfirmReq.getSeqNo());
        String customerId = customer.getSecondLevel().getCustomerId();
        AmLoanMainInfo amLoanMainInfo = extAmLoanMainInfoMapper.selectBySeqNo(loanConfirmReq.getSeqNo(), customerId, loanConfirmReq.getTenantId());
        if (null != amLoanMainInfo) {
            AmLoanAccountInfo amLoanAccountInfo = extAmLoanAccountInfoMapper.selectByLoanId(amLoanMainInfo.getLoanId(), customerId, loanConfirmReq.getTenantId());
            List<AmRepaymentPlanInfo> planInfoList = extAmRepaymentPlanInfoMapper.selectByLoanIdAndStatus(amLoanMainInfo.getLoanId()
                    , Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_1, TransBizConstant.LOAN_PLAN_STATUS_2, TransBizConstant.LOAN_PLAN_STATUS_0)
                    , customerId, loanConfirmReq.getTenantId());
            LoanConfirmRes res = loanService.buildRes(loanConfirmReq.getUserId(), amLoanMainInfo, planInfoList, amLoanAccountInfo.getAccountStatus());
            return res;
        }

        loanParamBean.setCustomer(customer);
        //3.查询渠道信息判断渠道是否存在
        logger.debug("3.查询渠道信息判断渠道是否存在,渠道：{}", loanConfirmReq.getChannel());
        PrChannelTable channel =
                channelTableService.getChannelTableByChannelIdAndStatus(TransBizConstant.YES_FLAG,
                        loanConfirmReq.getChannel(), loanConfirmReq.getTenantId());
        loanParamBean.setChannel(channel);
        //3.检查产品ID 若产品ID不存在 使用产品规则匹配 获取产品
        logger.debug("4.检查产品ID 若产品ID不存在 使用产品规则匹配 获取产品");
        if (StringUtils.isBlank(loanConfirmReq.getProductId())) {
            //产品规则
            logger.debug("产品ID不存在  根据规则获取产品号");
            loanConfirmReq.setProductId(getProductIdByRule(channel.getOrganizationId(), channel.getChannelId(), loanConfirmReq));

        }
        //4.根据产品Id获取产品信息
        logger.debug("5.根据产品Id获取产品信息,产品号：{}", loanConfirmReq.getProductId());
        PrProductTable product = productTableService.findProByProIdAndOrgId(loanConfirmReq.getProductId(),
                channel.getOrganizationId(), loanConfirmReq.getTenantId());
        loanParamBean.setProduct(product);
        // 修改客户客群
        List<CmCustomerExtInfo> cmCustomerExtInfos = extCmCustomerExtInfoMapper.queryCustomerExtInfos(customerId, loanConfirmReq.getTenantId(), BizTypeProducts.getSymbolProductId(product.getProductId()));
        if (CollectionUtils.isNotEmpty(cmCustomerExtInfos)) {
            customer.getSecondLevel().setCustomerGroup(cmCustomerExtInfos.get(0).getCustomerGroup());
        }
        logger.debug("校验还款方式，期数");
        validaAmortMethodAndTenor(product, loanConfirmReq, loanParamBean);

        //5.校验产品相关信息
        logger.debug("6.校验产品相关信息");
        validateProduct(product, loanConfirmReq);
        //6.获取放款卡信息
        logger.debug("7.获取放款卡信息,卡号：{}", loanConfirmReq.getCardNumber());
        CmCustomerBankcardInfo customerBank =
                bankCardService.getBankCardByCondition(customer.getSecondLevel().getCustomerId(), null,
                        loanConfirmReq.getCardNumber(), loanConfirmReq.getChannel(), loanConfirmReq.getTenantId());
        loanParamBean.setCustomerBank(customerBank);

        //7.获取机构业务日期
        logger.debug("12.获取机构信息（业务日期）");
        PrOrganizationTable org = organizationTableService.findByOrId(channel.getOrganizationId(), loanConfirmReq.getTenantId());
        loanParamBean.setOrg(org);

        //校验放款日期
        validateLoanDate(loanConfirmReq.getLoanDate(), org.getNextProcessingDate());

        //8.获取利率 罚息等数据ID（规则引擎）
        logger.debug("8.获取利率 罚息等数据ID（规则引擎）,并校验还款方式是否支持");
        getInterestAndOtherTableId(loanConfirmReq, loanParamBean);
        //9.还款日生成（规则引擎）
        logger.debug("9.还款日生成（规则引擎）");
        getPaymentDay(loanParamBean, loanConfirmReq, TransBizConstant.ONE);

        //10.账单日生成（规则引擎）
        logger.debug("10.账单日生成（规则引擎）");
        getStatementDay(loanParamBean, loanConfirmReq, TransBizConstant.ONE);

        //11.生成订单相关信息 贷款订单+账户信息+还款计划
        logger.debug("11.生成订单相关信息 贷款订单+账户信息+还款计划");

        //14.账单分期处理
        verifyFirstPaymentDate(loanParamBean,loanConfirmReq);

        return loanService.generatingOrderByLoanStaging(loanParamBean, loanConfirmReq);
    }

    @Override
    public LoanBillQueryRes loanBillQueryHandle(CommonAddReq commonReq) throws TxnException {
        LoanBillQueryRes res = new LoanBillQueryRes();
        String userId = commonReq.getUserId();
        String channel = commonReq.getChannel();
        String tenantId = commonReq.getTenantId();
        List<String> products = null;
        BizTypeEnum bizTypeEnum = null;
        if (StringUtils.isNotBlank(commonReq.getBizType())) {
            bizTypeEnum = BizTypeEnum.getByCode(commonReq.getBizType());
            products = BizTypeProducts.getByBizType(bizTypeEnum);
        }
        //1 获取客户2级信息
        CmCustomerSecondLevel customer = customerService.getSecondLevelByCondition(userId,
                channel, tenantId, null);
        if (customer == null) {
            logger.error("客户信息未找到，用户编号：{}，渠道：{}", userId, channel);
            throw new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
        }
        Integer statementDd = customer.getStatementDd();
        Integer paymentDd = customer.getPaymentDd();
        if (statementDd == null || statementDd == 0) {
            logger.info("客户还款日账单日不存在");
            return res;
        }

        // 户日终账务处理
        custBatchService.dealCustBatch(customer.getCustomerId(), customer.getOrganizationId(), customer.getTenantId());

        PrOrganizationTable organization =
                organizationTableService.findByOrId(customer.getOrganizationId(), tenantId);
        Date bussinessDate = organization.getNextProcessingDate();
        String productId = products.get(0);
        CurrentStatementBean bean = this.getCurrentStatement(productId , statementDd, paymentDd, bussinessDate);
        statementDd = DateUtils.getDayOfMonth(bean.getStatementDate());
        paymentDd = DateUtils.getDayOfMonth(bean.getPaymentDate());
        // 兴安借产品账单日与还款日特殊处理
        if(BizTypeProducts.getXAJSeriesProducts().contains(productId)){
            //通过账单日，获取当前业务日期月份的账单日期statementDate
            bean.setStatementDate(DateUtils.getThisMonthDate(bussinessDate, statementDd));
            //通过还款日，获取当前业务日期月份的还款日期paymentDate
            bean.setPaymentDate(DateUtils.getThisMonthDate(bussinessDate, paymentDd));
        }

        Date statementDate = bean.getStatementDate();
        Date paymentDate = bean.getPaymentDate();
        //下一期账单日
        Date nextStatementDate = DateUtils.getDateByMonth(statementDate, 1);
        //下一期还款日
        Date nextPaymentDate = DateUtils.getDateByMonth(paymentDate, 1);

        res.setPaymentDate(paymentDate);
        res.setPaymentDd(paymentDd);
        res.setCanStaging(TransBizConstant.NO_FLAG);
        res.setCanPayment(TransBizConstant.NO_FLAG);
        res.setStatementDate(statementDate);
        res.setNextPaymentDate(nextPaymentDate);
        res.setNextStatementDate(nextStatementDate);
        res.setNextStatementAmount(BigDecimal.ZERO);
        res.setToday(bussinessDate);
        res.setOrderCount(0);// 统计订单笔数
        res.setDelqCount(0);// 统计逾期笔数

        //获取退款溢缴款信息
        BigDecimal paymentAmount = BigDecimal.ZERO;//退款金额
        List<TlRefundLog> tlRefundLogList = extTlRefundLogMapper.selectByCustomerIdProductIdsAndTenantId(customer.getCustomerId(), customer.getTenantId(), products);
        if (CollectionUtils.isNotEmpty(tlRefundLogList)) {
            paymentAmount = tlRefundLogList.stream().filter(log -> log.getBusinessDate().compareTo(statementDate) >= 0)
                    .filter(log -> log.getBusinessDate().compareTo(bussinessDate) <= 0).map(TlRefundLog::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        }

        List<TlPaymentLog> paymentLogList = extTlPaymentLogMapper.selectByCustomerIdProductsAndSumStatus(customer.getCustomerId(), new String[]{TransBizConstant.PAYMENT_LOG_SUM_STATUS_S}, customer.getTenantId(), products);
        if (CollectionUtils.isNotEmpty(paymentLogList)) {
            paymentAmount = paymentAmount.add(paymentLogList.stream().filter(log -> log.getBusinessDate().compareTo(statementDate) >= 0)
                    .filter(log -> log.getBusinessDate().compareTo(bussinessDate) <= 0)
                    .map(TlPaymentLog::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add));
        }
        res.setPaymentAmount(paymentAmount);

        //4 根据条件获取订单账户信息 添加小额现金贷
        List<AmLoanAccountInfo> loanAccountList = extAmLoanAccountInfoMapper.selectByCustomerIdAndLoanTypeProductIdsAndNotStatus(tenantId,
                customer.getCustomerId(), channel, Arrays.asList(TransBizConstant.ACCOUNT_STATUS_6, TransBizConstant.ACCOUNT_STATUS_9),
                Arrays.asList(TransBizConstant.LOAN_TYPE_2, TransBizConstant.LOAN_TYPE_3, TransBizConstant.LOAN_TYPE_4, TransBizConstant.LOAN_TYPE_5, TransBizConstant.LOAN_TYPE_7), products);
        if (CollectionUtils.isEmpty(loanAccountList)) {
            res.setMonthStatus(TransBizConstant.MONTH_STATUS_1);
            return res;
        }

        List<String> loanIdList = new ArrayList<>();
        for (AmLoanAccountInfo amLoanAccountInfo : loanAccountList) {
            if (!loanIdList.contains(amLoanAccountInfo.getLoanId())) {
                loanIdList.add(amLoanAccountInfo.getLoanId());
            }
        }

        //4 按照 1 正常 2 逾期 查询还款计划 当期欠款按照累计计算 未抛账部分按照还款计划总金额计算
        List<AmRepaymentPlanInfo> planList =
                extAmRepaymentPlanInfoMapper.selectByLoanIdListAndStatus(
                        loanIdList, customer.getCustomerId(), customer.getTenantId(),
                        Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_1, TransBizConstant.LOAN_PLAN_STATUS_2, TransBizConstant.LOAN_PLAN_STATUS_0));
        if (CollectionUtils.isEmpty(planList)) {
            res.setMonthStatus(TransBizConstant.MONTH_STATUS_3);
            return res;
        }

        List<AmLoanMainInfo> loanMainInfoList = extAmLoanMainInfoMapper.selectByCusIdtProductIdsAndTenantId(customer.getCustomerId(), customer.getTenantId(), products);

        Map<String, AmLoanAccountInfo> acctInfomap = loanAccountList.stream().collect(Collectors.toMap(AmLoanAccountInfo::getLoanId, Function.identity()));

        //剩余应还本金
        BigDecimal remainAmt = loanAccountList.stream()
                .filter(accountInfo ->
                        Arrays.asList(TransBizConstant.ACCOUNT_STATUS_0, TransBizConstant.ACCOUNT_STATUS_1, TransBizConstant.ACCOUNT_STATUS_2)
                                .contains(accountInfo.getAccountStatus()))
                .map(AmLoanAccountInfo::getRemainPrincipal).reduce(BigDecimal.ZERO, BigDecimal::add);
        res.setRemainAmt(remainAmt);
        BigDecimal currentAmount = BigDecimal.ZERO;//当期总金额
        BigDecimal nextStatementAmount = BigDecimal.ZERO;//下期账单金额
        BigDecimal interestAmount = BigDecimal.ZERO;//当期利息金额
        BigDecimal principalAmount = BigDecimal.ZERO;//当期本金金额
        BigDecimal penaltyAmount = BigDecimal.ZERO;//罚息金额
        BigDecimal compoundAmount = BigDecimal.ZERO;//复利金额
        BigDecimal delqAmount = BigDecimal.ZERO;//逾期欠款
        BigDecimal penaltyDailyRate = BigDecimal.ZERO;//罚息日利率
        String hasDelq = TransBizConstant.NO_FLAG;//是否存在逾期 0 否 1 是
        String canStaging = TransBizConstant.YES_FLAG;//是否允许账单分期 0 否 1 是
        Date delqDate = null;
        BigDecimal totalAmount = BigDecimal.ZERO;//总欠款
        BigDecimal stagingAmount = BigDecimal.ZERO;//信用付欠款 判断是否账单分期

        for (AmRepaymentPlanInfo planInfo : planList) {

            AmLoanAccountInfo loanAccountInfo = acctInfomap.get(planInfo.getLoanId());
            //获取罚息日利率
            if (penaltyDailyRate.compareTo(BigDecimal.ZERO) == 0) {
                //查询贷款信息
                AmLoanMainInfo loanMainInfo = loanMainInfoList.stream().filter(loanMain -> planInfo.getLoanId().equals(loanMain.getLoanId())).findFirst().get();

                PrPenaltyRateTable penaltyRateTable = prPenaltyRateTableService.findByPenaltyId(loanMainInfo.getPenaltyTableId(), loanMainInfo.getOrganizationId(), loanMainInfo.getTenantId());
                penaltyDailyRate = penaltyRateTableService.getPenaltyDailyRate(loanMainInfo.getOrigDailyRate(), penaltyRateTable);
            }

            //计算欠款
            Date paymentDueDate = planInfo.getPaymentDueDate();
            if (paymentDate.compareTo(paymentDueDate) == 0) {//当期 说明还款计划状态绝对为已抛帐
                if (TransBizConstant.LOAN_PLAN_STATUS_1.equals(planInfo.getPlanStatus())) {
                    BigDecimal amt = planInfo.getPrincipalAmount().subtract(planInfo.getRepayPrincipal()).add(planInfo.getInterestAmount()).subtract(planInfo.getRepayInterest());
                    currentAmount = currentAmount.add(amt);
                    principalAmount = principalAmount.add(planInfo.getPrincipalAmount().subtract(planInfo.getRepayPrincipal()));
                    interestAmount = interestAmount.add(planInfo.getInterestAmount()).subtract(planInfo.getRepayInterest());
                    //总欠款
                    totalAmount = totalAmount.add(amt);

                    if (TransBizConstant.LOAN_TYPE_4.equals(loanAccountInfo.getLoanType())) {
                        stagingAmount = stagingAmount.add(amt);
                    }
                } else if (TransBizConstant.LOAN_PLAN_STATUS_2.equals(planInfo.getPlanStatus())) {
                    DebtBean debtBean = debtCalculationService.getBalance(loanAccountInfo, Collections.singletonList(planInfo), bussinessDate, true, null, null, false);
                    hasDelq = TransBizConstant.YES_FLAG;
                    canStaging = TransBizConstant.NO_FLAG;
                    delqDate = planInfo.getPaymentDueDate();
                    //总欠款
                    delqAmount = delqAmount.add(debtBean.getTotalAmount());
                    totalAmount = totalAmount.add(debtBean.getTotalAmount());
                    penaltyAmount = penaltyAmount.add(debtBean.getPenaltyAmount());
                    compoundAmount = compoundAmount.add(debtBean.getCompountAmount());
                    interestAmount = interestAmount.add(debtBean.getInterestAmount());
                    principalAmount = principalAmount.add(debtBean.getPrincipalAmount());
                }

            } else if (paymentDueDate.compareTo(nextPaymentDate) == 0) { //计算下一期账单欠款
                if (TransBizConstant.LOAN_PLAN_STATUS_1.equals(planInfo.getPlanStatus())) {
                    BigDecimal amt = planInfo.getPrincipalAmount().subtract(planInfo.getRepayPrincipal()).add(planInfo.getInterestAmount()).subtract(planInfo.getRepayInterest());
                    nextStatementAmount = nextStatementAmount.add(amt);
                    //总欠款
                    totalAmount = totalAmount.add(amt);
                    //principalAmount =  principalAmount.add(planInfo.getPrincipalAmount().subtract(planInfo.getRepayPrincipal()));
                    //interestAmount = interestAmount.add(planInfo.getInterestAmount()).subtract(planInfo.getRepayInterest());
                } else if (TransBizConstant.LOAN_PLAN_STATUS_0.equals(planInfo.getPlanStatus())) {
                    BigDecimal amt = planInfo.getTotalAmount();
                    nextStatementAmount = nextStatementAmount.add(amt);
                    //总欠款
                    totalAmount = totalAmount.add(amt);
                    //interestAmount = interestAmount.add(planInfo.getInterestAmount());
                    //principalAmount = principalAmount.add(planInfo.getPrincipalAmount());
                }
            } else {
                //还款日不等于当期的还款计算 只计算总欠款
                if (TransBizConstant.LOAN_PLAN_STATUS_2.equals(planInfo.getPlanStatus())) {
                    DebtBean debtBean = debtCalculationService.getBalance(loanAccountInfo, Collections.singletonList(planInfo), bussinessDate, true, null, null, false);
                    hasDelq = TransBizConstant.YES_FLAG;
                    canStaging = TransBizConstant.NO_FLAG;
                    delqDate = planInfo.getPaymentDueDate();
                    //总欠款
                    delqAmount = delqAmount.add(debtBean.getTotalAmount());
                    totalAmount = totalAmount.add(debtBean.getTotalAmount());
                    penaltyAmount = penaltyAmount.add(debtBean.getPenaltyAmount());
                    compoundAmount = compoundAmount.add(debtBean.getCompountAmount());
                    interestAmount = interestAmount.add(debtBean.getInterestAmount());
                    principalAmount = principalAmount.add(debtBean.getPrincipalAmount());
                } else if (TransBizConstant.LOAN_PLAN_STATUS_0.equals(planInfo.getPlanStatus())) {
                    //未来期次 按照还款计划计算
                    totalAmount = totalAmount.add(planInfo.getTotalAmount());
                    //interestAmount = interestAmount.add(planInfo.getInterestAmount());
                    //principalAmount = principalAmount.add(planInfo.getPrincipalAmount());
                }
            }
        }

        //6 是否允许账单分期判断
        if (bizTypeEnum != null && bizTypeEnum.getCanStaging()) {
            if (TransBizConstant.YES_FLAG.equals(canStaging)) {
                canStaging = checkLoanStaging(loanMainInfoList, statementDate, paymentDate, bussinessDate, stagingAmount);
            }
        } else {
            canStaging = TransBizConstant.NO_FLAG;
        }

        //7 是否允许还款校验
        String canPayment = checkPayment(statementDate, bussinessDate, hasDelq, currentAmount);

        if (TransBizConstant.YES_FLAG.equals(hasDelq)) {
            res.setPenaltyAmount(penaltyAmount);
            res.setCompoundAmount(compoundAmount);
            Integer delqDays = planList.stream().filter(plan -> TransBizConstant.LOAN_PLAN_STATUS_2.equals(plan.getPlanStatus()))
                    .map(AmRepaymentPlanInfo::getDelqDays).max(Integer::compareTo).get();
            res.setDelqDays(delqDays);
        }

        res.setTotalAmount(totalAmount);
        res.setCurrentAmount(currentAmount);
        res.setDelqAmount(delqAmount);
        res.setInterestAmount(interestAmount);
        res.setPrincipalAmount(principalAmount);
        res.setPenaltyDailyRate(penaltyDailyRate);
        res.setPaymentDd(paymentDd);
        res.setCanStaging(canStaging);
        res.setHasDelq(hasDelq);
        res.setDelqDate(delqDate);
        res.setCanPayment(canPayment);
        res.setNextStatementAmount(nextStatementAmount);
        res.setCanStagingAmount(stagingAmount);

        UnaryOperator<String> getMonthStatus = monthStatus -> {
            if (TransBizConstant.LOAN_PLAN_STATUS_0.equals(monthStatus)) {
                return TransBizConstant.MONTH_STATUS_1;
            } else if (TransBizConstant.LOAN_PLAN_STATUS_1.equals(monthStatus)) {
                if (bean.getPaymentDate().compareTo(paymentDate) == 0) {
                    return TransBizConstant.MONTH_STATUS_0;//当期
                } else {
                    return TransBizConstant.MONTH_STATUS_1;//未到期
                }
            } else if (TransBizConstant.LOAN_PLAN_STATUS_2.equals(monthStatus)) {
                return TransBizConstant.MONTH_STATUS_2;//逾期
            } else {
                return TransBizConstant.MONTH_STATUS_3;//结清
            }
        };

        String monthStatus = planList.stream().filter(
                plan -> Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_0, TransBizConstant.LOAN_PLAN_STATUS_1, TransBizConstant.LOAN_PLAN_STATUS_2).contains(plan.getPlanStatus()))
                .map(AmRepaymentPlanInfo::getPlanStatus).max(String::compareTo).map(getMonthStatus).orElse(TransBizConstant.MONTH_STATUS_3);
        res.setMonthStatus(monthStatus);
        // 统计订单笔数
        res.setOrderCount(Math.toIntExact(loanAccountList.stream().filter(loan -> !Arrays.asList(TransBizConstant.ACCOUNT_STATUS_7, TransBizConstant.ACCOUNT_STATUS_8).contains(loan.getAccountStatus())).count()));
        // 统计逾期笔数
        res.setDelqCount(Math.toIntExact(loanAccountList.stream().filter(loan -> TransBizConstant.ACCOUNT_STATUS_2.equalsIgnoreCase(loan.getAccountStatus())).count()));
        return res;
    }

    @Override
    public AllBillQueryRes allBillQueryHandle(AllBillQueryRep rep) throws TxnException {
        AllBillQueryRes res = new AllBillQueryRes();
        res.setSeqNo(rep.getSeqNo());
        res.setUserId(rep.getUserId());

        String userId = rep.getUserId();
        String channel = rep.getChannel();
        String tenantId = rep.getTenantId();
        //1 获取客户2级信息
        CmCustomerSecondLevel customer = customerService.getSecondLevelByCondition(userId,
                channel, tenantId, null);
        if (customer == null) {
            logger.error("客户信息未找到，用户编号：{}，渠道：{}", userId, channel);
            throw new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
        }
        if (customer.getStatementDd() == null || customer.getStatementDd() == 0) {
            logger.info("客户还款日账单日不存在");
            return res;
        }

        // 客户日终账务处理
        custBatchService.dealCustBatch(customer.getCustomerId(), customer.getOrganizationId(), customer.getTenantId());

        //先获取当期账单日的还款日
        PrOrganizationTable organization =
                organizationTableService.findByOrId(customer.getOrganizationId(), tenantId);
        Date bussinessDate = organization.getNextProcessingDate();
        int paymentDd = customer.getPaymentDd();
        int statementDd = customer.getStatementDd();

        //2 获取开始月份，结束月份账单日对应的还款日
        Date startStatementDate = DateUtils.getThisMonthDate(rep.getStartMonth(), DateUtils.YEAR_MONTH_PATTERN, statementDd);
        Date startPaymentDate = this.getCurrentPaymentDate(startStatementDate, paymentDd);
        Date endStatementDate = DateUtils.getThisMonthDate(rep.getEndMonth(), DateUtils.YEAR_MONTH_PATTERN, statementDd);
        Date endPaymentDate = this.getCurrentPaymentDate(endStatementDate, paymentDd);

        //查询订单
        List<AmLoanAccountInfo> loanAccountList = extAmLoanAccountInfoMapper.selectByCustomerIdAndLoanTypeAndNotStatus(rep.getTenantId(),
                customer.getCustomerId(), channel, Arrays.asList(TransBizConstant.LOAN_STATUS_6, TransBizConstant.LOAN_STATUS_9),
                Arrays.asList(TransBizConstant.LOAN_TYPE_2, TransBizConstant.LOAN_TYPE_3, TransBizConstant.LOAN_TYPE_4, TransBizConstant.LOAN_TYPE_5, TransBizConstant.LOAN_TYPE_7));


        if (CollectionUtils.isEmpty(loanAccountList)) {
            return res;
        }
        List<String> loanIdList = new ArrayList<>();
        for (AmLoanAccountInfo amLoanAccountInfo : loanAccountList) {
            if (!loanIdList.contains(amLoanAccountInfo.getLoanId())) {
                loanIdList.add(amLoanAccountInfo.getLoanId());
            }
        }
        //根据订单号，还款日期 未到期 正常 逾期状态的还款计划  还款日排序
        List<AmRepaymentPlanInfo> planInfos =
                extAmRepaymentPlanInfoMapper.selectByLoanIdAndStartDateAndEndDate(customer.getCustomerId(), rep.getTenantId(),
                        loanIdList, startPaymentDate, endPaymentDate, Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_0, TransBizConstant.LOAN_PLAN_STATUS_1, TransBizConstant.LOAN_PLAN_STATUS_2,
                                TransBizConstant.LOAN_PLAN_STATUS_7, TransBizConstant.LOAN_PLAN_STATUS_8));
        if (CollectionUtils.isEmpty(planInfos)) {
            logger.info("未获取到还款计划");
            return res;
        }
        //月份
        List<MonthBill> monthBillList = packageMonthBill(planInfos, loanAccountList, bussinessDate, paymentDd, statementDd);
        res.setMonthBillList(monthBillList);
        return res;
    }

    /**
     * 组装月账单
     *
     * @param planInfos       还款计划
     * @param loanAccountList 订单账户
     * @param businessDate    业务日期
     * @param paymentDd       还款日
     * @param statementDd     账单日
     * @return
     */
    private List<MonthBill> packageMonthBill(List<AmRepaymentPlanInfo> planInfos, List<AmLoanAccountInfo> loanAccountList, Date businessDate, int paymentDd, int statementDd) {
        CurrentStatementBean bean = this.getCurrentStatement(planInfos.get(0).getProductId(), statementDd, paymentDd, businessDate);
        Date currentPaymentDate = bean.getPaymentDate();
        Map<Date, List<AmRepaymentPlanInfo>> monthPlanMap = planInfos.stream().collect(Collectors.groupingBy(AmRepaymentPlanInfo::getPaymentDueDate));
        return monthPlanMap.entrySet().stream().map(
                entry -> {
                    MonthBill monthBill = new MonthBill();

                    Date paymentDueDate = entry.getKey();
                    monthBill.setPaymentDueDate(paymentDueDate);

                    //转成对应的账单月
                    Date statementDate = this.getCurrentStatementDate(paymentDueDate, statementDd);
                    String month = DateUtils.format(statementDate, DateUtils.YEAR_MONTH_PATTERN);
                    monthBill.setMonth(month);
                    monthBill.setStatmentDate(statementDate);


                    List<AmRepaymentPlanInfo> planList = entry.getValue();
                    //计算欠款
                    BigDecimal monthAmount = planList.stream().map(plan -> countPlanAmount(plan, loanAccountList, businessDate, currentPaymentDate)).reduce(BigDecimal.ZERO, BigDecimal::add);
                    monthBill.setMonthAmount(monthAmount);

                    UnaryOperator<String> getMonthStatus = monthStatus -> {
                        if (TransBizConstant.LOAN_PLAN_STATUS_0.equals(monthStatus)) {
                            return TransBizConstant.MONTH_STATUS_1;
                        } else if (TransBizConstant.LOAN_PLAN_STATUS_1.equals(monthStatus)) {
                            if (currentPaymentDate.compareTo(paymentDueDate) == 0) {
                                return TransBizConstant.MONTH_STATUS_0;//当期
                            } else {
                                return TransBizConstant.MONTH_STATUS_1;//未到期
                            }
                        } else if (TransBizConstant.LOAN_PLAN_STATUS_2.equals(monthStatus)) {
                            return TransBizConstant.MONTH_STATUS_2;//逾期
                        } else {
                            return TransBizConstant.MONTH_STATUS_3;//结清
                        }
                    };
                    //还款计划状态优先级按2，1，0，然后转成对应的月状态，其他的为固定结清状态
                    String monthStatus = planList.stream().filter(
                            plan -> Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_0, TransBizConstant.LOAN_PLAN_STATUS_1, TransBizConstant.LOAN_PLAN_STATUS_2).contains(plan.getPlanStatus()))
                            .map(AmRepaymentPlanInfo::getPlanStatus).max(String::compareTo).map(getMonthStatus).orElse(TransBizConstant.MONTH_STATUS_3);
                    monthBill.setMonthStatus(monthStatus);

                    //获取最大逾期天数
                    int delqDays = planList.stream().map(AmRepaymentPlanInfo::getDelqDays).max(Integer::compareTo).orElse(0);
                    monthBill.setDelqDays(delqDays);
                    return monthBill;
                }
        ).collect(Collectors.toList());
    }

    /**
     * 待还账单查询
     *
     * @param queryReq
     * @return
     * @throws TxnException
     */
    @Override
    public AllRemainBillQueryRes allRemainBillQueryHandle(CommonAddReq queryReq) throws TxnException {
        AllRemainBillQueryRes res = new AllRemainBillQueryRes();
        res.setSeqNo(queryReq.getSeqNo());
        res.setUserId(queryReq.getUserId());

        String userId = queryReq.getUserId();
        String channel = queryReq.getChannel();
        String tenantId = queryReq.getTenantId();
        List<String> products = null;
        if (StringUtils.isNotBlank(queryReq.getBizType())) {
            products = BizTypeProducts.getByBizType(BizTypeEnum.getByCode(queryReq.getBizType()));
        }
        //1 获取客户2级信息
        CmCustomerSecondLevel customer = customerService.getSecondLevelByCondition(userId,
                channel, tenantId, null);
        if (customer == null) {
            logger.error("客户信息未找到，用户编号：{}，渠道：{}", userId, channel);
            throw new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
        }
        if (customer.getStatementDd() == null || customer.getStatementDd() == 0) {
            logger.info("客户还款日账单日不存在");
            return res;
        }
        String customerId = customer.getCustomerId();
        // 客户日终账务处理
        custBatchService.dealCustBatch(customerId, customer.getOrganizationId(), customer.getTenantId());

        //先获取当期账单日的还款日
        PrOrganizationTable organization =
                organizationTableService.findByOrId(customer.getOrganizationId(), tenantId);
        Date bussinessDate = organization.getNextProcessingDate();
        int paymentDd = customer.getPaymentDd();
        int statementDd = customer.getStatementDd();
        String productId = products.get(0);
        statementDd = iPaymentHandler.getStatementDayByProductId(bussinessDate, statementDd, productId);

        paymentDd = iPaymentHandler.getPaymentDayByProductId(bussinessDate, paymentDd, productId);
        //查询订单
        List<AmLoanAccountInfo> loanAccountList = extAmLoanAccountInfoMapper.selectByCustomerIdAndLoanTypeProductIdsAndNotStatus(queryReq.getTenantId(),
                customerId, channel, Arrays.asList(TransBizConstant.LOAN_STATUS_6, TransBizConstant.LOAN_STATUS_7, TransBizConstant.LOAN_STATUS_8, TransBizConstant.LOAN_STATUS_9),
                Arrays.asList(TransBizConstant.LOAN_TYPE_2, TransBizConstant.LOAN_TYPE_3, TransBizConstant.LOAN_TYPE_4, TransBizConstant.LOAN_TYPE_5, TransBizConstant.LOAN_TYPE_7),products);

        if (CollectionUtils.isEmpty(loanAccountList)) {
            return res;
        }
        List<String> loanIdList = new ArrayList<>();
        for (AmLoanAccountInfo amLoanAccountInfo : loanAccountList) {
            if (!loanIdList.contains(amLoanAccountInfo.getLoanId())) {
                loanIdList.add(amLoanAccountInfo.getLoanId());
            }
        }
        //根据订单号，还款日期 未到期 正常 逾期状态的还款计划  还款日排序
        List<AmRepaymentPlanInfo> planInfos =
                extAmRepaymentPlanInfoMapper.selectByLoanIdListAndStatus(loanIdList, customerId, queryReq.getTenantId(),
                        Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_0, TransBizConstant.LOAN_PLAN_STATUS_1, TransBizConstant.LOAN_PLAN_STATUS_2));
        if (CollectionUtils.isEmpty(planInfos)) {
            logger.info("未获取到还款计划");
            return res;
        }
        //月份
        List<MonthBill> monthBillList = packageMonthBill(planInfos, loanAccountList, bussinessDate, paymentDd, statementDd);
        BigDecimal totalAmount = monthBillList.stream().map(MonthBill::getMonthAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        res.setTotalAmount(totalAmount);
        res.setMonthBillList(monthBillList);
        // 逾期余额计算
        BigDecimal overdueBalance = BigDecimal.ZERO;
        // 罚息余额计算
        BigDecimal penaltyBalance = BigDecimal.ZERO;
        // 利息计算
        BigDecimal interestAmount = BigDecimal.ZERO;
        List<AmAccountMainInfo> amAccountMainInfoList = extAmAccountMainInfoMapper.selectAccountsByproductIds(customer.getTenantId(), customerId, null,
                Arrays.asList(TransBizConstant.ACCOUNT_TYPE_04, TransBizConstant.ACCOUNT_TYPE_06, TransBizConstant.ACCOUNT_TYPE_11),
                Collections.singletonList(TransBizConstant.ACCOUNT_STATUS_2), products);
        if (CollectionUtils.isNotEmpty(amAccountMainInfoList)) {
            overdueBalance = overdueBalance.add(amAccountMainInfoList.stream()
                            .filter(acc -> TransBizConstant.ACCOUNT_TYPE_06.equalsIgnoreCase(acc.getAccountType()))
                            .map(AmAccountMainInfo::getCurrBalance)
                            .reduce(BigDecimal.ZERO, BigDecimal::add));
            // 当期的罚息
            penaltyBalance = penaltyBalance.add(amAccountMainInfoList.stream()
                            .filter(acc -> TransBizConstant.ACCOUNT_TYPE_11.equalsIgnoreCase(acc.getAccountType()))
                            .map(AmAccountMainInfo::getCurrBalance)
                            .reduce(BigDecimal.ZERO, BigDecimal::add));
            // 当期利息
            interestAmount = interestAmount.add(amAccountMainInfoList.stream()
                            .filter(acc -> TransBizConstant.ACCOUNT_TYPE_04.equalsIgnoreCase(acc.getAccountType()))
                            .map(AmAccountMainInfo::getCurrBalance)
                            .reduce(BigDecimal.ZERO, BigDecimal::add));

        }
        List<AmInterestAccrualInfo> amInterestAccrualInfoList = extAmInterestAccrualInfoMapper.selectByCustWithProducts(customerId, tenantId,
                Collections.singletonList(TransBizConstant.ACCOUNT_STATUS_1), products);
        // 所有的罚息
        penaltyBalance = penaltyBalance.add(amInterestAccrualInfoList.stream()
                .filter(acc->TransBizConstant.INTEREST_ACCRUAL_TYPE_10.equalsIgnoreCase(acc.getAccountType()))
                .map(AmInterestAccrualInfo::getAccrualTotalAccruInterest)
                .reduce(BigDecimal.ZERO, BigDecimal::add));
        // 所有的利息
        interestAmount = interestAmount.add(amInterestAccrualInfoList.stream()
                .filter(acc->TransBizConstant.INTEREST_ACCRUAL_TYPE_66.equalsIgnoreCase(acc.getAccountType()))
                .map(AmInterestAccrualInfo::getAccrualTotalAccruInterest)
                .reduce(BigDecimal.ZERO, BigDecimal::add));
        res.setInterestAmount(interestAmount);
        res.setOverdueBalance(overdueBalance);
        res.setPenaltyBalance(penaltyBalance);
        return res;
    }


    @Override
    public BillDetailQueryRes billDetailQueryRepHandle(BillDetailQueryRep rep) throws TxnException {
        BillDetailQueryRes res = new BillDetailQueryRes();
        res.setSeqNo(rep.getSeqNo());
        res.setBillMonth(rep.getQueryMonth());

        List<BillDetail> billDetails = new ArrayList<>();

        String userId = rep.getUserId();
        String channel = rep.getChannel();
        String tenantId = rep.getTenantId();
        //1 获取客户2级信息
        CmCustomerSecondLevel customer = customerService.getSecondLevelByCondition(userId,
                channel, tenantId, null);
        if (customer == null) {
            logger.error("客户信息未找到，用户编号：{}，渠道：{}", userId, channel);
            throw new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
        }
        if (customer.getStatementDd() == null || customer.getStatementDd() == 0) {
            logger.info("客户还款日账单日不存在");
            res.setCanStaging(TransBizConstant.NO_FLAG);
            res.setHasDelq(TransBizConstant.NO_FLAG);
            res.setBillTotalAmount(BigDecimal.ZERO);
            return res;
        }

        //2 获取业务日期
        PrOrganizationTable organization =
                organizationTableService.findByOrId(customer.getOrganizationId(), tenantId);
        Date bussinessDate = organization.getNextProcessingDate();

        //2 获取查询月份账单日对应的还款日
        int paymentDd = customer.getPaymentDd();
        int statementDd = customer.getStatementDd();
        //String productId = products.get(0);
        //statementDd = iPaymentHandler.getStatementDayByProductId(bussinessDate, statementDd, productId);
        //paymentDd = iPaymentHandler.getPaymentDayByProductId(bussinessDate, paymentDd, productId);
        Date queryStatementDate = DateUtils.getThisMonthDate(rep.getQueryMonth(), DateUtils.YEAR_MONTH_PATTERN, statementDd);
        Date queryPaymentDate = this.getCurrentPaymentDate(queryStatementDate, paymentDd);
        // 产品类型加上 5 小额现金贷
        List<AmLoanAccountInfo> loanAccountList = extAmLoanAccountInfoMapper.selectByCustomerIdAndLoanTypeAndNotStatus(rep.getTenantId(),
                customer.getCustomerId(), channel, Collections.singletonList(TransBizConstant.ACCOUNT_STATUS_9),
                Arrays.asList(TransBizConstant.LOAN_TYPE_2, TransBizConstant.LOAN_TYPE_3, TransBizConstant.LOAN_TYPE_4, TransBizConstant.LOAN_TYPE_5, TransBizConstant.LOAN_TYPE_7));
        if (CollectionUtils.isEmpty(loanAccountList)) {
            logger.info("未获取到订单账户信息，客户号：{}", customer.getCustomerId());
            res.setPaymentDate(queryPaymentDate);//未获取到传业务日期
            res.setCanStaging(TransBizConstant.NO_FLAG);
            res.setHasDelq(TransBizConstant.NO_FLAG);
            res.setBillTotalAmount(BigDecimal.ZERO);
            return res;
        }

        String hasDelq = TransBizConstant.NO_FLAG;
        String canStaging = TransBizConstant.YES_FLAG;

        //判断客户是否有逾期
        for (AmLoanAccountInfo info : loanAccountList) {
            if (TransBizConstant.ACCOUNT_STATUS_2.equals(info.getAccountStatus())) {
                hasDelq = TransBizConstant.YES_FLAG;
                canStaging = TransBizConstant.NO_FLAG;
                break;
            }
        }


        //4 根据订单 还款日期 查询还款计划
        List<String> loanIdList = new ArrayList<>();
        for (AmLoanAccountInfo loanAccountInfo : loanAccountList) {
            loanIdList.add(loanAccountInfo.getLoanId());
        }
        List<AmRepaymentPlanInfo> planInfoList =
                extAmRepaymentPlanInfoMapper.selectByLoanIdList(customer.getCustomerId(), rep.getTenantId(), loanIdList, queryPaymentDate);
        if (CollectionUtils.isEmpty(planInfoList)) {
            logger.info("未获取到还款计划信息");
            res.setPaymentDate(queryPaymentDate);//月还款日
            res.setCanStaging(TransBizConstant.NO_FLAG);
            res.setHasDelq(TransBizConstant.NO_FLAG);
            res.setBillTotalAmount(BigDecimal.ZERO);
            return res;
        }

        List<AmLoanMainInfo> loanMainInfoList = extAmLoanMainInfoMapper.selectByCusIdtAndTenantId(customer.getCustomerId(), customer.getTenantId());


        BigDecimal currAmount = BigDecimal.ZERO;//未结清金额
        BigDecimal clearAmount = BigDecimal.ZERO;//已还金额
        BigDecimal stagingAmount = BigDecimal.ZERO;//信用付欠款 判断是否账单分期

        Map<String, AmLoanAccountInfo> infoMap = loanAccountList.stream().collect(Collectors.toMap(AmLoanAccountInfo::getLoanId, Function.identity()));
        for (AmRepaymentPlanInfo planInfo : planInfoList) {

            AmLoanAccountInfo loanAccountInfo = infoMap.get(planInfo.getLoanId());

            res.setPaymentDate(planInfo.getPaymentDueDate());//还款日

            BillDetail billDetail = new BillDetail();

            //获取订单信息
            AmLoanMainInfo mainInfo = loanMainInfoList.stream().filter(loanMain -> planInfo.getLoanId().equals(loanMain.getLoanId())).findFirst().get();
            billDetail.setLoanId(planInfo.getLoanId());
            billDetail.setTxnTime(mainInfo.getTxnTime());
            billDetail.setMemo(mainInfo.getMemoid());//备注
            billDetail.setProductId(mainInfo.getProductId());
            billDetail.setProductType(mainInfo.getLoanType());//产品类型
            if (StringUtils.isNotBlank(mainInfo.getMerchantId())) {
                PmMerchantTable pmMerchantTable = extPmMerchantTableMapper.selectByTenantIdAndMerchantId(planInfo.getTenantId(), mainInfo.getMerchantId());
                if (null != pmMerchantTable) {
                    billDetail.setMerchantId(mainInfo.getMerchantId());
                    billDetail.setMerchantName(pmMerchantTable.getMerchantName());
                }
            } else {
                billDetail.setMerchantId("");
                billDetail.setMerchantName("");
            }
            billDetail.setLoanAmount(mainInfo.getLoanAmount());

            BigDecimal principalAmount = BigDecimal.ZERO;//本金
            BigDecimal interestAmount = BigDecimal.ZERO;//利息
            BigDecimal feeAmount = BigDecimal.ZERO;//费用
            BigDecimal penaltyAmount = BigDecimal.ZERO;//罚息
            BigDecimal compoundAmount = BigDecimal.ZERO;//复利
            BigDecimal billAmount = BigDecimal.ZERO;//订单当期金额
            if (TransBizConstant.LOAN_PLAN_STATUS_1.equals(planInfo.getPlanStatus())) {
                //计算欠款
                DebtBean debtBean = debtCalculationService.getBalance(loanAccountInfo, Collections.singletonList(planInfo), bussinessDate, null, null, null, false);
                currAmount = currAmount.add(debtBean.getTotalAmount());
                principalAmount = debtBean.getPrincipalAmount();//本金
                feeAmount = debtBean.getFeeAmount();//费用哪个
                interestAmount = debtBean.getInterestAmount(); //利息金额

                billAmount = debtBean.getTotalAmount();
            } else if (TransBizConstant.LOAN_PLAN_STATUS_0.equals(planInfo.getPlanStatus())) {
                //未到期取还款计划金额
                principalAmount = planInfo.getPrincipalAmount();
                feeAmount = planInfo.getFeeAmount();
                interestAmount = planInfo.getInterestAmount();
                currAmount = currAmount.add(planInfo.getTotalAmount());
                billAmount = planInfo.getTotalAmount();
            } else if (TransBizConstant.LOAN_PLAN_STATUS_2.equals(planInfo.getPlanStatus())) {
                canStaging = TransBizConstant.NO_FLAG;
                DebtBean debtBean = debtCalculationService.getBalance(infoMap.get(planInfo.getLoanId()), Collections.singletonList(planInfo), bussinessDate, true, null, null, false);
                currAmount = currAmount.add(debtBean.getTotalAmount());

                principalAmount = debtBean.getPrincipalAmount();
                feeAmount = debtBean.getFeeAmount();
                interestAmount = debtBean.getInterestAmount();
                compoundAmount = debtBean.getCompountAmount();
                penaltyAmount = debtBean.getPenaltyAmount();

                billAmount = debtBean.getTotalAmount();
            } else {
                //已结清的获取还款计划 已还款部分金额
                principalAmount = planInfo.getRepayPrincipal();
                feeAmount = planInfo.getRepayFee();
                interestAmount = planInfo.getRepayInterest();
                compoundAmount = planInfo.getRepayCompoundInterest();
                penaltyAmount = planInfo.getRepayPenaltyInterest();

                billAmount = principalAmount.add(feeAmount).add(interestAmount).add(compoundAmount).add(penaltyAmount);
                clearAmount = clearAmount.add(billAmount);
            }

            billDetail.setBillAmount(billAmount);
            billDetail.setPrincipalAmount(principalAmount);//本金
            billDetail.setInterestAmount(interestAmount);//利息
            billDetail.setFeeAmount(feeAmount);//费用
            billDetail.setCompoundAmount(compoundAmount);//复利
            billDetail.setPenaltyAmount(penaltyAmount);//罚息
            billDetail.setTenor(mainInfo.getTotalTenor());//总期数
            billDetail.setBillingTenor(planInfo.getBillingTenor());//当前期数
            billDetail.setBillStatus(planInfo.getPlanStatus());
            if (TransBizConstant.LOAN_TYPE_4.equals(loanAccountInfo.getLoanType())) {
                stagingAmount = stagingAmount.add(billAmount);
            }

            billDetails.add(billDetail);
        }
        //有欠款只显示欠款总金额
        if (currAmount.compareTo(BigDecimal.ZERO) > 0) {
            res.setBillTotalAmount(currAmount);
        } else {
            res.setBillTotalAmount(clearAmount);
        }

        //5 是否允许账单分期判断
        if (canStaging.equals(TransBizConstant.YES_FLAG)) {
            canStaging = checkLoanStaging(loanMainInfoList, queryStatementDate, queryPaymentDate, bussinessDate, stagingAmount);
        }

        //6 组装返回参数
        res.setCanStaging(canStaging);
        res.setHasDelq(hasDelq);
        res.setBillDetailList(billDetails);

        return res;
    }

    @Override
    public StagingLoanDetailQueryRes stagingLoanDetailQueryRepHandle(StagingLoanDetailQueryRep req) throws TxnException {
        StagingLoanDetailQueryRes res = new StagingLoanDetailQueryRes();
        res.setSeqNo(req.getSeqNo());
        res.setUserId(req.getUserId());
        res.setLoanId(req.getLoanId());

        CmCustomerSecondLevel customer = customerService.getSecondLevelByCondition(req.getUserId(),
                req.getChannel(), req.getTenantId(), null);
        if (customer == null) {
            logger.error("客户信息未找到，用户编号：{}，渠道：{}", req.getUserId(), req.getChannel());
            throw new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
        }

        String loanId = req.getLoanId();
        String tenantId = req.getTenantId();
        List<TlLoanStagingMapping> tlLoanStagingMappings = extTlLoanStagingMappingMapper.selectByCustIdAndStagingLoanId(customer.getCustomerId(), tenantId, loanId);
        if (CollectionUtils.isEmpty(tlLoanStagingMappings)) {
            logger.info("未获取到账单分期关联关系表数据");
            res.setLoanAmount(BigDecimal.ZERO);
            res.setStagingCount(0);
            res.setLoanTenor(0);
            return res;
        }

        res.setLoanAmount(tlLoanStagingMappings.get(0).getLoanAmount());
        res.setStagingCount(tlLoanStagingMappings.size());
        res.setLoanTenor(tlLoanStagingMappings.get(0).getTotalTenor());
        List<StagingLoanRes> stagingLoanResList = new ArrayList<>(tlLoanStagingMappings.size());
        for (TlLoanStagingMapping mapping : tlLoanStagingMappings) {
            StagingLoanRes stagingLoanRes = new StagingLoanRes();
            stagingLoanRes.setLoanId(mapping.getOriginalLoanId());
            stagingLoanRes.setStagingAmount(mapping.getStagingAmount());
            AmLoanMainInfo loanMainInfo = extAmLoanMainInfoMapper.selectByLoanIdAndCusId(mapping.getOriginalLoanId(), customer.getCustomerId(), mapping.getTenantId());
            stagingLoanRes.setMemo(loanMainInfo.getMemoid());
            if (StringUtils.isNotBlank(loanMainInfo.getMerchantId())) {
                PmMerchantTable pmMerchantTable = extPmMerchantTableMapper.selectByTenantIdAndMerchantId(loanMainInfo.getTenantId(), loanMainInfo.getMerchantId());
                if (null != pmMerchantTable) {
                    stagingLoanRes.setMerchantName(pmMerchantTable.getMerchantName());
                }
            } else {
                stagingLoanRes.setMerchantName("");
            }
            String txnTime = DateUtils.format(loanMainInfo.getTxnTime(), "yyyy-MM-dd HH:mm:ss");
            stagingLoanRes.setTxnTime(txnTime);

            stagingLoanResList.add(stagingLoanRes);
        }
        res.setStagingLoanResList(stagingLoanResList);

        return res;
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public CancelOrderQueryRes cancelOrderHandle(CancelOrderRep cancelOrderRep) throws Exception {
        CancelOrderQueryRes res = new CancelOrderQueryRes();
        res.setSeqNo(cancelOrderRep.getSeqNo());

        //1 获取客户2级信息
        CmCustomerSecondLevel customer = customerService.getSecondLevelByCondition(cancelOrderRep.getUserId(),
                cancelOrderRep.getChannel(), cancelOrderRep.getTenantId(), null);
        if (customer == null) {
            logger.error("客户信息未找到，用户编号：{}，渠道：{}", cancelOrderRep.getUserId(), cancelOrderRep.getChannel());
            throw new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
        }

        //2 获取订单信息
        AmLoanMainInfo amLoanMainInfo = extAmLoanMainInfoMapper.selectByLoanIdAndCusId(cancelOrderRep.getLoanId(), customer.getCustomerId(), cancelOrderRep.getTenantId());
        if (null == amLoanMainInfo) {
            logger.warn("取消订单接口，订单不存在，订单编号 ：" + cancelOrderRep.getLoanId());
            throw new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
        }

        // 3 业务日期
        PrOrganizationTable organization =
                organizationTableService.findByOrId(customer.getOrganizationId(), customer.getTenantId());
        Date businessDate = organization.getNextProcessingDate();

        boolean sendSms = true;
        // 4 执行取消流程
        if (DailyAccountEnum.T24_PRODUCTID.getCode().contains(amLoanMainInfo.getProductId())){
            // 兴安借产品撤销处理逻辑
            disbursementLogService.cancelOrder(businessDate, cancelOrderRep.getOperStatus(), cancelOrderRep.getSeqNo(), cancelOrderRep.getTenantId(), TransBizConstant.SYS_OPRATE);
            sendSms = false;
        } else {
            this.cancelOrder(cancelOrderRep, customer, amLoanMainInfo, businessDate);
        }
        res.setValueDate(DateUtils.formatDate(businessDate));
        res.setSendSms(sendSms);
        res.setSunStatus(TransBizConstant.GATEWAY_DEAL_SUC);
        return res;
    }

    /**
     * 非兴安借产品取消订单业务逻辑处理
     * @param cancelOrderRep
     * @param customer 客户2级信息
     * @param amLoanMainInfo 订单信息
     * @throws Exception
     */
    private void cancelOrder(CancelOrderRep cancelOrderRep, CmCustomerSecondLevel customer, AmLoanMainInfo amLoanMainInfo, Date bussinessDate) throws Exception {
        //1 获取贷款账户信息
        AmLoanAccountInfo amLoanAccountInfo = extAmLoanAccountInfoMapper.selectByLoanId(cancelOrderRep.getLoanId(), customer.getCustomerId(), cancelOrderRep.getTenantId());

        if (!TransBizConstant.ACCOUNT_STATUS_0.equals(amLoanAccountInfo.getAccountStatus()) &&
                !TransBizConstant.ACCOUNT_STATUS_1.equals(amLoanAccountInfo.getAccountStatus())) {
            logger.error(TxnRespCode.CANCEL_LOAN_STATUS_NOT_ALLOW.getMsg());
            throw new TxnBizException(TxnRespCode.CANCEL_LOAN_STATUS_NOT_ALLOW.getCode(), TxnRespCode.CANCEL_LOAN_STATUS_NOT_ALLOW.getUserTips());
        }

        //2 获取产品信息
        PrProductTable prProductTable = productTableService.findProByProIdAndOrgId(
                amLoanMainInfo.getProductId(), amLoanMainInfo.getOrganizationId(), amLoanMainInfo.getTenantId());
        if (null == prProductTable) {
            logger.error("取消订单接口，产品不存在");
            throw new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
        }
        if (TransBizConstant.YES_FLAG.equals(prProductTable.getRemitWhenLoan())) {
            logger.error(TxnRespCode.CANCEL_REMIT_WHEN_LOAN_NOT_ALLOW.getMsg());
            throw new TxnBizException(TxnRespCode.CANCEL_REMIT_WHEN_LOAN_NOT_ALLOW.getCode(), TxnRespCode.CANCEL_REMIT_WHEN_LOAN_NOT_ALLOW.getUserTips());
        }

        // 3 取消订单
        CancelSaveBean cancelSaveBean = loanService.cancelOrder(amLoanMainInfo.getLoanId(),
                customer.getCustomerId(), customer.getTenantId(), bussinessDate, cancelOrderRep.getChangeLimit());
        cancelSaveBean.getTlTransactionInfoList().forEach(t -> {
            t.setPostingDate(bussinessDate);
            t.setSeqNo(cancelOrderRep.getSeqNo());
        });

        // 4 批量保存取消订单信息
        loanService.saveCancleBean(cancelSaveBean);
    }

    /**
     * 查询，校验，更新客户额度信息
     *
     * @param loanConfirmReq 贷款确认请求参数
     * @param customerId     客户ID
     * @param organizationId 机构号
     * @param productId      产品ID
     * @param businessDate   业务日期
     * @return void
     * @author fan.yang
     * date: 2019/10/24 0024 上午 11:01
     */
    private void verifyAndModifyCustomerLimit(LoanConfirmReq loanConfirmReq, String customerId, String organizationId
            , String productId, Date businessDate) throws TxnBizException {
        //获取客户额度信息
        CmCustomerLimitInfo cmCustomerLimitInfo = customerLimitInfoService.getCustomerLimitInfo(customerId, organizationId, loanConfirmReq.getChannel(), productId, loanConfirmReq.getContractNumber(), loanConfirmReq.getLoanAmount(), loanConfirmReq.getTenantId());
        //校验客户额度
        customerLimitInfoService.verifyCustomer(cmCustomerLimitInfo, loanConfirmReq.getLoanAmount(), businessDate);
        //更新客户额度信息
        customerLimitInfoService.modifyCustomerLimit(cmCustomerLimitInfo, loanConfirmReq.getLoanAmount(), loanConfirmReq.getUserId());
    }

    /**
     * 验证放款日期
     *
     * @param loanDate     贷款请求
     * @param businessDate 业务日期
     */
    private void validateLoanDate(Date loanDate, Date businessDate) {
        //放款日期不得大于业务日期
        if (loanDate != null && loanDate.compareTo(businessDate) > 0) {
            String errMsg = TxnRespCode.LOAN_DATE_ERROR.getFormatMsg(loanDate, businessDate);
            logger.error(errMsg);
            throw new TxnRuntimeException(TxnRespCode.LOAN_DATE_ERROR.getCode(), TxnRespCode.LOAN_DATE_ERROR.getUserTips());
        }
    }

    /**
     * 订单验证
     * 订单号与外部合同号（必传一个）
     *
     * @param loanId         订单id
     * @param contractNumber 合同号
     * @throws TxnBizException 自定义业务异常
     */
    private void validateLoanPlanQueryReq(String loanId, String contractNumber) throws TxnBizException {
        if (StringUtils.isBlank(loanId) && StringUtils.isBlank(contractNumber)) {
            logger.error(TransErrMsgConstant.NO_LOAN_CONTRACT);
            //贷款订单号号与外部合同号都是空
            throw new TxnBizException(TxnRespCode.NOT_EMPTY.getCode(),
                    TxnRespCode.NOT_EMPTY.getUserTips());
        }
    }

    /**
     * 是否允许账单分期判断
     *
     * @param loanMainInfoList
     * @param statementDate
     * @param paymentDate
     * @param bussinessDate
     * @param stagingAmount
     * @return
     */
    private String checkLoanStaging(List<AmLoanMainInfo> loanMainInfoList, Date statementDate, Date paymentDate, Date bussinessDate, BigDecimal stagingAmount) {
        //出账单才允许分期
        if (bussinessDate.compareTo(statementDate) < 0) {
            return TransBizConstant.NO_FLAG;
        }

        //账单可分期金额>0 才允许分期
        if (stagingAmount.compareTo(BigDecimal.ZERO) <= 0) {
            return TransBizConstant.NO_FLAG;
        }
        //当月已做账单分期
        if (loanMainInfoList.stream().filter(loan -> TransBizConstant.LOAN_TYPE_3.equals(loan.getLoanType()))
                .anyMatch(loan -> loan.getPostingDate().compareTo(statementDate) >= 0 && loan.getPostingDate().compareTo(paymentDate) <= 0)) {
            return TransBizConstant.NO_FLAG;
        }

        return TransBizConstant.YES_FLAG;
    }

    /**
     * 是否允许还款校验
     *
     * @param statementDate 账单
     * @param bussinessDate
     * @param hasDelq
     * @param currentAmount
     * @return
     */
    private String checkPayment(Date statementDate, Date bussinessDate, String hasDelq, BigDecimal currentAmount) {
        //逾期允许还款
        if (TransBizConstant.YES_FLAG.equals(hasDelq)) {
            return TransBizConstant.YES_FLAG;
        }

        //出账单才允许还款
        if (bussinessDate.compareTo(statementDate) < 0) {
            return TransBizConstant.NO_FLAG;
        }

        //当期欠款>0才允许还款
        if (currentAmount.compareTo(BigDecimal.ZERO) <= 0) {
            return TransBizConstant.NO_FLAG;
        }
        return TransBizConstant.YES_FLAG;
    }

    private BigDecimal countPlanAmount(AmRepaymentPlanInfo plan, List<AmLoanAccountInfo> loanAccountList, Date bussinessDate, Date currentPaymentDate) {
        BigDecimal totalAmount = BigDecimal.ZERO;

        Map<String, AmLoanAccountInfo> map = loanAccountList.stream().collect(Collectors.toMap(AmLoanAccountInfo::getLoanId,
                amLoanAccountInfo -> amLoanAccountInfo));

        AmLoanAccountInfo loanAccountInfo = map.get(plan.getLoanId());

        if (TransBizConstant.LOAN_PLAN_STATUS_0.equals(plan.getPlanStatus())) {
            //未到期 欠款直接拿还款计划总金额
            totalAmount = totalAmount.add(plan.getTotalAmount());
        } else if (TransBizConstant.LOAN_PLAN_STATUS_2.equals(plan.getPlanStatus())) {
            //计算欠款
            DebtBean debtBean = debtCalculationService.getBalance(loanAccountInfo, Collections.singletonList(plan), bussinessDate, true, null, null, false);
            totalAmount = totalAmount.add(debtBean.getTotalAmount());
        } else if (TransBizConstant.LOAN_PLAN_STATUS_1.equals(plan.getPlanStatus())) {
            //计算欠款
            totalAmount = totalAmount.add(plan.getTotalAmount()).subtract(plan.getRepayPrincipal()).subtract(plan.getRepayInterest());
        }
        return totalAmount;
    }


    /**
     * 验证商户是否存在
     *
     * @param tenantId   租户
     * @param merchantId 商户id
     * @throws TxnBizException
     */
    private void verifyMerchant(String tenantId, String merchantId) throws TxnBizException {
        if (StringUtils.isNotBlank(merchantId)) {
            PmMerchantTable pmMerchantTable =
                    pmMerchantTableService.getByMerchanId(tenantId, merchantId);
        }
    }

    private void validateRemitWayAndAccount(PrProductTable prProductTable) throws TxnBizException {
        if (StringUtils.isBlank(prProductTable.getRemitWay()) || StringUtils.isBlank(prProductTable.getRemitAccount())) {
            logger.error(TxnRespCode.NOT_EMPTY.getMsg());
            throw new TxnBizException(TxnRespCode.NOT_EMPTY.getCode(), TxnRespCode.NOT_EMPTY.getUserTips());
        }

        //校验放款路径 目前只支持放款到 放款到指定账户
        if (!TransBizConstant.REMIT_WAY_1.equals(prProductTable.getRemitWhenLoan())) {
            logger.error(TxnRespCode.LOAN_REMIT_WAY_NOT.getMsg());
            throw new TxnBizException(TxnRespCode.LOAN_REMIT_WAY_NOT.getCode(), TxnRespCode.LOAN_REMIT_WAY_NOT.getUserTips());
        }
    }

    private CmCustomerBankcardInfo getBankcardInfo(CustomerRes customer, LoanConfirmReq loanConfirmReq, PrProductTable prProductTable) throws TxnException {
        CmCustomerBankcardInfo cmCustomerBankcardInfo = new CmCustomerBankcardInfo();
        logger.debug("7.获取放款卡信息");
        if (TransBizConstant.REMIT_WAY_1.equals(prProductTable.getRemitWay()) &&
                TransBizConstant.REMIT_ACCOUT_1.equals(prProductTable.getRemitAccount())) {
            cmCustomerBankcardInfo = bankCardService.getBankCardByCondition(customer.getSecondLevel().getCustomerId(), null,
                    loanConfirmReq.getCardNumber(), loanConfirmReq.getChannel(), loanConfirmReq.getTenantId());
        } else if (TransBizConstant.REMIT_WAY_1.equals(prProductTable.getRemitWay()) &&
                TransBizConstant.REMIT_ACCOUT_2.equals(prProductTable.getRemitAccount())) {
            //查询合作方结算户
            PmPartnerInfo pmPartnerInfo = pmPartnerInfoService.getByPartnerId(prProductTable.getTenantId(), prProductTable.getPartnerId());
            cmCustomerBankcardInfo.setTenantId(pmPartnerInfo.getTenantId());
            if (StringUtils.isBlank(pmPartnerInfo.getSettleAcct())) {
                logger.error(TxnRespCode.NOT_EMPTY.getMsg() + ",指定放款到合作方结算户，结算户不能为空");
                throw new TxnBizException(TxnRespCode.NOT_EMPTY.getCode(), TxnRespCode.NOT_EMPTY.getUserTips());
            }
            cmCustomerBankcardInfo.setCardNumber(pmPartnerInfo.getSettleAcct());//合作伙伴结算户
            cmCustomerBankcardInfo.setOrganizationId(pmPartnerInfo.getOrganizationId());
        } else if (TransBizConstant.REMIT_WAY_1.equals(prProductTable.getRemitWay()) &&
                TransBizConstant.REMIT_ACCOUT_3.equals(prProductTable.getRemitAccount())) {
            if (StringUtils.isNotBlank(loanConfirmReq.getMerchantId())) {
                //商户结算户
                PmMerchantTable pmMerchantTable =
                        pmMerchantTableService.getByMerchanId(loanConfirmReq.getTenantId(), loanConfirmReq.getMerchantId());
                cmCustomerBankcardInfo.setTenantId(pmMerchantTable.getTenantId());
                if (StringUtils.isBlank(pmMerchantTable.getSettleAcct())) {
                    logger.error(TxnRespCode.NOT_EMPTY.getMsg() + ",指定放款到商户结算户，结算户不能为空");
                    throw new TxnBizException(TxnRespCode.NOT_EMPTY.getCode(), TxnRespCode.NOT_EMPTY.getUserTips());
                }
                cmCustomerBankcardInfo.setCardNumber(pmMerchantTable.getSettleAcct());//合作伙伴结算户
                cmCustomerBankcardInfo.setOrganizationId(pmMerchantTable.getOrganizationId());
            } else {
                logger.error(TxnRespCode.NOT_EMPTY.getMsg() + ",指定放款到商户结算户，商户ID不能为空");
                throw new TxnBizException(TxnRespCode.NOT_EMPTY.getCode(), TxnRespCode.NOT_EMPTY.getUserTips());
            }
        }

        return cmCustomerBankcardInfo;
    }


    /**
     * 合作方额度校验，更新额度
     *
     * @param loanConfirmReq
     * @param product
     * @throws TxnBizException
     */
    private void verifyAndModifyPantnerLimit(LoanConfirmReq loanConfirmReq, PrProductTable product) throws TxnException {
        //需要更新合作方额度的产品才做检查
        if (TransBizConstant.UPDATE_DEPOSIT_LIMIT_1.equals(product.getUpdateDepositLimit())) {
            //校验合作方额度
            PmPartnerInfo pmPartnerInfo = pmPartnerInfoService.getByPartnerId(product.getTenantId(), product.getPartnerId());
            logger.debug("合作方总可用额度：{},当日可用额度：{}", pmPartnerInfo.getAvailableLimit(), pmPartnerInfo.getDailyAvailableLimit());

            //更新合作方额度(放款失败需要恢复额度)
            PartnerLimitAdjustmentReq req = new PartnerLimitAdjustmentReq();
            req.setSeqNo(loanConfirmReq.getSeqNo());
            req.setAdjustmentType(TransBizConstant.ADJUSTMENT_TYPE_0);
            req.setAmout(loanConfirmReq.getLoanAmount());
            req.setPartnerId(pmPartnerInfo.getPartnerId());
            req.setTenantId(pmPartnerInfo.getTenantId());
            TxnRespResult<PartnerLimitAdjustmentRes> res;
            try {
                res = anyTxnParamService.partnerLimitAdjustment(req);
            } catch (Exception e) {
                logger.error("调用合作方额度调整接口出现异常", e);
                throw new TxnBizException(TxnRespCode.ERROR.getUserTips());
            }

            if (null == res || !TransBizConstant.ADJUSTMENT_STATUS_S.equals(res.getData().getSumStatus())) {
                logger.error("调用合作方额度调整接口 更新失败");
                throw new TxnBizException(TxnRespCode.ERROR.getUserTips());
            }
        }
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void cancelOrderWithDisbursementLog(TlDisbursementLog tlDisbursementLog) throws TxnBizException {
        String tenantId = tlDisbursementLog.getTenantId();
        String loanId = tlDisbursementLog.getLoanId();
        String customerId = tlDisbursementLog.getCustomerId();
        PrOrganizationTable organization =
                organizationTableService.findByOrId(tlDisbursementLog.getOrganizationId(), tenantId);
        Date businessDate = organization.getNextProcessingDate();
        // 取消订单
        CancelSaveBean cancelSaveBean = loanService.cancelOrder(loanId, customerId, tenantId, businessDate, Constant.YES_FLAG);
        loanService.saveCancleBean(cancelSaveBean);
        TlDisbursementLog updateLog = new TlDisbursementLog();
        updateLog.setId(tlDisbursementLog.getId());
        updateLog.setCustomerId(customerId);
        updateLog.setTenantId(tenantId);
        updateLog.setCancelStatus(TransBizConstant.YES_FLAG);
        updateLog.setCollateDate(businessDate);
        updateLog.setCompleteTime(DateUtils.getBusinessTime(new Date(), businessDate));
        // 兴安借产品他行卡放款撤销处理逻辑
        this.cancelOrderByXaj(updateLog, tlDisbursementLog);
        List<TlDisbursementLog> list = new ArrayList<>();
        list.add(updateLog);
        AmLoanMainInfo amLoanMainInfo = extAmLoanMainInfoMapper.selectByLoanIdAndCusId(loanId, customerId, tenantId);
        try {
            smsService.sendSmsMq(amLoanMainInfo,TransBizConstant.SMS_TYPE_CANCEL_ORDER);
        } catch (Exception e) {
            logger.warn("取消放款短信接口异常：{}", e);
        }
        try {
            // 通知api-server放款状态更新
            this.disbursementStatus(tlDisbursementLog, updateLog.getCollateDate(), amLoanMainInfo.getContractNumber());
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("通知api异常", e);
        }
        extTlDisbursementLogMapper.batchUpdateLog(list);
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public ModifyLoanInfoRes modifyLoanInfoHandle(ModifyLoanInfoReq modifyLoanInfoReq) throws Exception {
        CmCustomerSecondLevel secondLevel = customerService
                .getSecondLevelByCondition(modifyLoanInfoReq.getUserId(), modifyLoanInfoReq.getChannel(), modifyLoanInfoReq.getTenantId(), null);
        String loanId = modifyLoanInfoReq.getLoanId();
        AmLoanAccountInfo amLoanAccountInfo = extAmLoanAccountInfoMapper.selectByLoanId(loanId, secondLevel.getCustomerId(), secondLevel.getTenantId());
        AmLoanMainInfo amLoanMainInfo = extAmLoanMainInfoMapper.selectByLoanIdAndCusId(loanId, secondLevel.getCustomerId(), secondLevel.getTenantId());
        TlDisbursementLog log = extTlDisbursementLogMapper.selectDisbursementByCondition(loanId, secondLevel.getCustomerId(), modifyLoanInfoReq.getTenantId());
        if (!TransBizConstant.DISBURSEMENT_LOG_SUM_STATUS_F.equals(log.getSumStatus())) {
            logger.error("订单未放款失败");
            throw new TxnBizException(TxnRespCode.LOAN_IS_NOT_FAIL.getCode(), TxnRespCode.LOAN_IS_NOT_FAIL.getUserTips());
        }

        if (!TransBizConstant.LOAN_STATUS_9.equals(amLoanAccountInfo.getAccountStatus())) {
            logger.error("原订单未取消");
            throw new TxnBizException(TxnRespCode.LOAN_IS_NOT_CANCEL.getCode(), TxnRespCode.LOAN_IS_NOT_CANCEL.getUserTips());
        }

        //获取客户额度信息
        String limitId = iCreditLimitMappingService.getLimitId(secondLevel.getOrganizationId(), secondLevel.getChannel(), amLoanAccountInfo.getProductId(), secondLevel.getTenantId());
        CmCustomerLimitInfo cmCustomerLimitInfo = extCmCustomerLimitInfoMapper.selectByChannel(secondLevel.getCustomerId(), secondLevel.getOrganizationId(), limitId, amLoanMainInfo.getContractNumber(), secondLevel.getTenantId(), secondLevel.getChannel());
        //更新客户额度信息
        customerLimitInfoService.modifyCustomerLimit(cmCustomerLimitInfo, amLoanMainInfo.getLoanAmount(), modifyLoanInfoReq.getUserId());

        TlDisbursementLog newLog = new TlDisbursementLog();
        BeanMapping.copy(log, newLog);
        newLog.setId(segmentService.getId(TransBizConstant.TL_DISBURSEMENT_ID));
        newLog.setPayeeAccountType(modifyLoanInfoReq.getPayeeAccountType());
        newLog.setPayeeAccount(modifyLoanInfoReq.getPayeeAccount());
        newLog.setPayeeBank(modifyLoanInfoReq.getPayeeBank());
        newLog.setPayeeName(modifyLoanInfoReq.getPayeeName());
        newLog.setSumStatus(TransBizConstant.DISBURSEMENT_LOG_SUM_STATUS_D);
        newLog.setRefNo(sequenceNumberService.generateRefNo(Constant.SEQUENCE_LENGTH));
        PrOrganizationTable org = organizationTableService.findByOrId(TransBizConstant.DEFAULT_ORG_ID, modifyLoanInfoReq.getTenantId());
        newLog.setBusinessDate(org.getNextProcessingDate());
        newLog.setEffectDate(org.getNextProcessingDate());
        newLog.setCancelStatus(TransBizConstant.CANCLE_STATUS_0);
        newLog.setCreateTime(null);
        newLog.setUpdateTime(null);
        newLog.setRecordVersionNumber(TransBizConstant.INIT_RECORD_VERSION_NUMBER);
        extTlDisbursementLogMapper.insertSelective(newLog);


        TlDisbursementLog updateLog = new TlDisbursementLog();
        updateLog.setId(log.getId());
        StringBuilder sb = new StringBuilder(loanId);
        int rStr = r.ints(100, (999 + 1)).limit(1).findFirst().getAsInt();
        sb.replace(0, 4, "m" + rStr);
        updateLog.setLoanId(sb.toString());
        updateLog.setCustomerId(secondLevel.getCustomerId());
        updateLog.setTenantId(modifyLoanInfoReq.getTenantId());
        extTlDisbursementLogMapper.updateLog(updateLog);

        AmLoanMainInfo updateLoan = new AmLoanMainInfo();
        updateLoan.setLoanId(loanId);
        updateLoan.setTenantId(modifyLoanInfoReq.getTenantId());
        updateLoan.setCustomerId(secondLevel.getCustomerId());
        updateLoan.setCustomerAccountNumber(modifyLoanInfoReq.getPayeeAccount());
        updateLoan.setSerialNumber(modifyLoanInfoReq.getSeqNo());
        extAmLoanMainInfoMapper.updateByLoanId(updateLoan);

        AmLoanAccountInfo updateLoanAcct = new AmLoanAccountInfo();
        updateLoanAcct.setLoanId(loanId);
        updateLoanAcct.setTenantId(modifyLoanInfoReq.getTenantId());
        updateLoanAcct.setCustomerId(secondLevel.getCustomerId());
        updateLoanAcct.setCastTenor(0);
        updateLoanAcct.setAccountStatus(TransBizConstant.LOAN_STATUS_0);
        extAmLoanAccountInfoMapper.updateByLoanId(updateLoanAcct);

        ModifyLoanInfoRes res = new ModifyLoanInfoRes();
        res.setUserId(modifyLoanInfoReq.getUserId());
        res.setLoanId(modifyLoanInfoReq.getLoanId());
        return res;
    }

    /**
     * 同步API放款失败信息
     *
     * @param syncLoanFailInfoReq
     * @return CommonRes
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public CommonRes syncLoanFailInfoHandle(SyncLoanFailInfoReq syncLoanFailInfoReq) throws TxnBizException {

        CmCustomerSecondLevel secondLevel = customerService
                .getSecondLevelByCondition(syncLoanFailInfoReq.getUserId(), syncLoanFailInfoReq.getChannel(), syncLoanFailInfoReq.getTenantId(), null);

        // 判断是否已经同步过
        Long id = exttlloanfaillogmapper.selectIdBySeqNo(syncLoanFailInfoReq.getSeqNo(), syncLoanFailInfoReq.getTenantId(), secondLevel.getCustomerId());
        if (id != null) {
            CommonRes res = new CommonRes();
            res.setUserId(syncLoanFailInfoReq.getUserId());
            return res;
        }

        PrOrganizationTable org = organizationTableService.findByOrId(secondLevel.getOrganizationId(), syncLoanFailInfoReq.getTenantId());
        TlLoanFailLog tlLoanFailLog = new TlLoanFailLog();
        try {
            tlLoanFailLog.setId(segmentService.getId(TransBizConstant.TL_LOAN_FAIL_LOG_ID));
        } catch (SegmentException e) {
            logger.error("主键生成错误", e);
            throw new TxnBizException(ErrorMsgConstant.ERROR_ID);
        }
        tlLoanFailLog.setTenantId(syncLoanFailInfoReq.getTenantId());
        tlLoanFailLog.setChannel(syncLoanFailInfoReq.getChannel());
        tlLoanFailLog.setUserId(syncLoanFailInfoReq.getUserId());
        tlLoanFailLog.setCustomerId(secondLevel.getCustomerId());
        tlLoanFailLog.setContractNumber(syncLoanFailInfoReq.getContractNumber());
        tlLoanFailLog.setCustomerName(syncLoanFailInfoReq.getCustomerName());
        tlLoanFailLog.setLoanDate(syncLoanFailInfoReq.getLoanDate());
        tlLoanFailLog.setLoanAmount(syncLoanFailInfoReq.getLoanAmount());
        tlLoanFailLog.setActualRate(syncLoanFailInfoReq.getActualRate());
        tlLoanFailLog.setStatus(syncLoanFailInfoReq.getStatus());
        tlLoanFailLog.setErrorCode(syncLoanFailInfoReq.getErrorCode());
        tlLoanFailLog.setErrorMsg(syncLoanFailInfoReq.getErrorMsg());
        tlLoanFailLog.setSeqNo(syncLoanFailInfoReq.getSeqNo());
        tlLoanFailLog.setBusinessDate(org.getNextProcessingDate());
        tlLoanFailLog.setRecordVersionNumber(TransBizConstant.INIT_RECORD_VERSION_NUMBER);
        tlLoanFailLog.setProductId(syncLoanFailInfoReq.getProductId());
        exttlloanfaillogmapper.insertSelective(tlLoanFailLog);

        CommonRes res = new CommonRes();
        res.setUserId(syncLoanFailInfoReq.getUserId());
        return res;
    }

    /**
     * 受托支付参数校验
     *
     * @param loanConfirmReq
     */
    private void validateEntrustedPayment(LoanConfirmReq loanConfirmReq) throws TxnBizException {
        if (StringUtils.isBlank(loanConfirmReq.getPayeeBank())) {
            logger.error(TxnRespCode.NOT_EMPTY.getMsg());
            throw new TxnBizException(TxnRespCode.NOT_EMPTY.getCode(), TxnRespCode.NOT_EMPTY.getUserTips());
        }

        if (StringUtils.isBlank(loanConfirmReq.getCardNumber())) {
            logger.error(TxnRespCode.NOT_EMPTY.getMsg());
            throw new TxnBizException(TxnRespCode.NOT_EMPTY.getCode(), TxnRespCode.NOT_EMPTY.getUserTips());
        }

        if (StringUtils.isBlank(loanConfirmReq.getPayeeBankName())) {
            logger.error(TxnRespCode.NOT_EMPTY.getMsg());
            throw new TxnBizException(TxnRespCode.NOT_EMPTY.getCode(), TxnRespCode.NOT_EMPTY.getUserTips());
        }

        if (StringUtils.isBlank(loanConfirmReq.getPayeeName())) {
            logger.error(TxnRespCode.NOT_EMPTY.getMsg());
            throw new TxnBizException(TxnRespCode.NOT_EMPTY.getCode(), TxnRespCode.NOT_EMPTY.getUserTips());
        }
    }

    /**
     * 校验封锁码
     *
     * @param blockCode
     */
    @Override
    public void validateBlockCode(String blockCode, String tenantId) throws TxnException {
        if (StringUtils.isBlank(blockCode)) {
            return;
        }
        PrBlockCode prBlockCode = blockCodeService.findByBlockCode(tenantId, blockCode);
        if (TransBizConstant.AUTHORIZATION_ATTRIBUTE_0.equals(prBlockCode.getAuthorizationAttribute())) {
            throw new TxnException(TxnRespCode.BLOCK_CODE_REJECT.getCode(), TxnRespCode.BLOCK_CODE_REJECT.getUserTips());
        }

    }

    /**
     * 获取当期账单日和还款日
     *
     * @param productId
     * @param paymentDd
     * @param statementDd
     * @param bussinessDate
     * @return
     */
    @Override
    public CurrentStatementBean getCurrentStatement(String productId, int statementDd, int paymentDd, Date bussinessDate) {
        paymentDd = iPaymentHandler.getPaymentDayByProductId(bussinessDate, paymentDd, productId);
        statementDd = iPaymentHandler.getStatementDayByProductId(bussinessDate, statementDd, productId);
        CurrentStatementBean bean = new CurrentStatementBean();
        //通过账单日，获取当前业务日期月份的账单日期statementDate
        Date statementDate = DateUtils.getThisMonthDate(bussinessDate, statementDd);
        //当期账单日为当前业务日期前一个账单日,如果业务日期<当月账单日，则账单日前后推一个月
        if (bussinessDate.compareTo(statementDate) < 0) {
            statementDate = DateUtils.getDateByMonth(statementDate, -1);
        }
        bean.setStatementDate(statementDate);
        //通过还款日paymentDd，获取账单日期statementDate月份的还款日期paymentDate
        Date paymentDate = DateUtils.getThisMonthDate(statementDate, paymentDd);
        if (statementDate.compareTo(paymentDate) > 0) {
            //如果账单日>还款日，则还款日往后推一个月
            paymentDate = DateUtils.getDateByMonth(paymentDate, 1);
        }
        bean.setPaymentDate(paymentDate);
        return bean;
    }

    /**
     * 获取账单日获取对应的还款日
     *
     * @param statementDate
     * @param paymentDd
     * @return
     */
    @Override
    public Date getCurrentPaymentDate(Date statementDate, int paymentDd) {
        Date paymentDate = DateUtils.getThisMonthDate(statementDate, paymentDd);
        if (statementDate.compareTo(paymentDate) > 0) {
            //如果账单日>还款日，则还款日往后推一个月
            paymentDate = DateUtils.getDateByMonth(paymentDate, 1);
        }
        return paymentDate;
    }

    /**
     * 获取还款日获取对应的账单日
     *
     * @param paymentDate
     * @param statementDd
     * @return
     */
    private Date getCurrentStatementDate(Date paymentDate, int statementDd) {
        Date statementDate = DateUtils.getThisMonthDate(paymentDate, statementDd);
        if (statementDate.compareTo(paymentDate) > 0) {
            //如果账单日>还款日，则账单日往前推一个月
            statementDate = DateUtils.getDateByMonth(statementDate, -1);
        }
        return statementDate;
    }

    @Override
    public BillDetailsInquiryRes billDetailsInquir(BillDetailsInquiryReq req) throws Exception {
        BillDetailsInquiryRes res = new BillDetailsInquiryRes();
        res.setSeqNo(req.getSeqNo());

        List<BillDetail> billDetails = new ArrayList<>();

        String userId = req.getUserId();
        String channel = req.getChannel();
        String tenantId = req.getTenantId();
        String queryMonth = req.getQueryMonth();
        res.setBillMonth(queryMonth);


        //1 获取客户2级信息
        CmCustomerSecondLevel customer = customerService.getSecondLevelByCondition(userId,
                channel, tenantId, null);
        if (customer == null) {
            logger.error("客户信息未找到，用户编号：{}，渠道：{}", userId, channel);
            throw new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
        }
        Integer statementDd = customer.getStatementDd();
        if (statementDd == null || statementDd == 0) {
            logger.info("客户还款日账单日不存在");
            res.setCanStaging(TransBizConstant.NO_FLAG);
            res.setHasDelq(TransBizConstant.NO_FLAG);
            res.setBillTotalAmount(BigDecimal.ZERO);
            return res;
        }

        //2 获取业务日期
        PrOrganizationTable organization =
                organizationTableService.findByOrId(customer.getOrganizationId(), tenantId);
        Date bussinessDate = organization.getNextProcessingDate();
        List<String> products;
        BizTypeEnum bizTypeEnum = null;
        if (StringUtils.isNotBlank(req.getBizType())) {
            bizTypeEnum = BizTypeEnum.getByCode(req.getBizType());
            products = BizTypeProducts.getByBizType(BizTypeEnum.getByCode(req.getBizType()));
        } else {
            products = req.getProductIds();
        }
        //2 获取查询月份账单日对应的还款日
        int paymentDd = customer.getPaymentDd();
        String productId = products.get(0);
        CurrentStatementBean bean = this.getCurrentStatement(productId, statementDd, paymentDd, bussinessDate);
        paymentDd = DateUtils.getDayOfMonth(bean.getPaymentDate());
        statementDd = DateUtils.getDayOfMonth(bean.getStatementDate());

        Date queryStatementDate = DateUtils.getThisMonthDate(queryMonth, DateUtils.YEAR_MONTH_PATTERN, statementDd);
        Date queryPaymentDate = this.getCurrentPaymentDate(queryStatementDate, paymentDd);
        Date lastQueryStatementDate = DateUtils.addMonth(queryStatementDate,-1);

        res.setPaymentDate(queryPaymentDate);//月还款日
        res.setCanStaging(TransBizConstant.NO_FLAG);
        if (bean.getPaymentDate().compareTo(queryPaymentDate) == 0) {
            res.setMonthStatus(TransBizConstant.MONTH_STATUS_0);
        } else {
            res.setMonthStatus(TransBizConstant.MONTH_STATUS_1);
        }

        //获取退款溢缴款信息
        BigDecimal refundAmount = BigDecimal.ZERO;//退款金额
        List<TlRefundLog> tlRefundLogList = extTlRefundLogMapper.selectByCustomerIdProductIdsAndTenantId(customer.getCustomerId(), customer.getTenantId(), products);
        if (CollectionUtils.isNotEmpty(tlRefundLogList)) {
            refundAmount = tlRefundLogList.stream().filter(log -> log.getBusinessDate().compareTo(queryStatementDate) >= 0)
                    .filter(log -> log.getBusinessDate().compareTo(queryPaymentDate) <= 0).map(TlRefundLog::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        }
        res.setRefundAmount(refundAmount);


        BigDecimal queryMonthPaymentAmount = BigDecimal.ZERO;//当月还款金额
        BigDecimal queryMonthRefundAmount = BigDecimal.ZERO;//当月退款金额
        List<TlPaymentAllocationLog> paymentLogList = extTlPaymentAllocationLogMapper.selectAll09WithProducts( customer.getCustomerId(), customer.getTenantId(), TransBizConstant.ACCOUNT_TYPE_09,products);
        if (CollectionUtils.isNotEmpty(paymentLogList)) {
            queryMonthPaymentAmount = queryMonthPaymentAmount.add(paymentLogList.stream().filter(payment -> !TransBizConstant.REPAYMENT_TYPE_REFUND.equals(payment.getPaymentType()))
                    .filter(payment -> !TransBizConstant.REPAYMENT_TYPE_STAGING.equals(payment.getPaymentType()))
                    .filter(payment -> payment.getPostingDate().compareTo(lastQueryStatementDate)>=0)
                    .filter(payment -> payment.getPostingDate().compareTo(queryStatementDate)<0)
                    .map(TlPaymentAllocationLog::getPostingAmount).reduce(BigDecimal.ZERO, BigDecimal::add));
            queryMonthRefundAmount = queryMonthRefundAmount.add(paymentLogList.stream().filter(payment -> TransBizConstant.REPAYMENT_TYPE_REFUND.equals(payment.getPaymentType()))
                    .filter(payment -> payment.getPostingDate().compareTo(lastQueryStatementDate)>=0)
                    .filter(payment -> payment.getPostingDate().compareTo(queryStatementDate)<0)
                    .map(TlPaymentAllocationLog::getPostingAmount).reduce(BigDecimal.ZERO, BigDecimal::add));
        }
        res.setQueryMonthPaymentAmount(queryMonthPaymentAmount);
        res.setQueryMonthRefundAmount(queryMonthRefundAmount);


        List<AmLoanAccountInfo> loanAccountList = extAmLoanAccountInfoMapper.selectByCustomerIdAndLoanTypeProductIdsAndNotStatus(req.getTenantId(),
                customer.getCustomerId(), channel, Collections.singletonList(TransBizConstant.ACCOUNT_STATUS_9),
                Arrays.asList(TransBizConstant.LOAN_TYPE_2, TransBizConstant.LOAN_TYPE_3, TransBizConstant.LOAN_TYPE_4, TransBizConstant.LOAN_TYPE_5, TransBizConstant.LOAN_TYPE_7), products);
        if (CollectionUtils.isEmpty(loanAccountList)) {
            logger.info("未获取到订单账户信息，客户号：{}", customer.getCustomerId());
            return res;
        }

        String hasDelq = TransBizConstant.NO_FLAG;
        String canStaging = TransBizConstant.YES_FLAG;

        //判断客户是否有逾期
        if (loanAccountList.stream().anyMatch(loan -> TransBizConstant.LOAN_STATUS_2.equals(loan.getAccountStatus()))) {
            hasDelq = TransBizConstant.YES_FLAG;
            canStaging = TransBizConstant.NO_FLAG;
        }
        res.setHasDelq(hasDelq);


        //4 根据订单 还款日期 查询还款计划
        List<String> loanIdList = new ArrayList<>();
        for (AmLoanAccountInfo loanAccountInfo : loanAccountList) {
            loanIdList.add(loanAccountInfo.getLoanId());
        }

        List<AmLoanMainInfo> loanMainInfoList = extAmLoanMainInfoMapper.selectByCusIdtProductIdsAndTenantId(customer.getCustomerId(),
                req.getTenantId(), products);
        loanMainInfoList = loanMainInfoList.stream().filter(loan -> loanIdList.contains(loan.getLoanId())).collect(Collectors.toList());

        List<AmRepaymentPlanInfo> planList =
                extAmRepaymentPlanInfoMapper.selectByLoanIdListAndStatus(
                        loanIdList, customer.getCustomerId(), customer.getTenantId(),
                        Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_1, TransBizConstant.LOAN_PLAN_STATUS_2, TransBizConstant.LOAN_PLAN_STATUS_0
                                , TransBizConstant.LOAN_PLAN_STATUS_6, TransBizConstant.LOAN_PLAN_STATUS_7, TransBizConstant.LOAN_PLAN_STATUS_8));
        if (CollectionUtils.isEmpty(planList)) {
            logger.info("未获取到所有还款计划信息");
            return res;
        }


        //获取历史数据
        BigDecimal lastBillBalance = BigDecimal.ZERO;//上月账单余额
        BigDecimal circleInterestAmount = BigDecimal.ZERO;//循环利息
        BigDecimal penaltyAmount = BigDecimal.ZERO;//逾期罚息
        BigDecimal delqStagingInterestAmount = BigDecimal.ZERO;//上月账单罚息、复利估算到本期还款日

        List<AmRepaymentPlanInfo> historyPlanInfoList = planList.stream().filter(plan -> plan.getPaymentDueDate().compareTo(queryPaymentDate) < 0).collect(Collectors.toList());

        if (CollectionUtils.isNotEmpty(historyPlanInfoList)) {
            for (AmRepaymentPlanInfo planInfo : historyPlanInfoList) {
                //获取订单信息
                AmLoanMainInfo mainInfo = loanMainInfoList.stream().filter(loan -> planInfo.getLoanId().equals(loan.getLoanId())).findFirst().get();
                if (TransBizConstant.LOAN_PLAN_STATUS_2.equals(planInfo.getPlanStatus())) {
                    BigDecimal pentyAmt = BigDecimal.ZERO;
                    List<AmInterestAccrualInfo> accruAcctList = extAmInterestAccrualInfoMapper.selectByStatusAndType(
                            planInfo.getLoanId(), planInfo.getCustomerId(), tenantId, Collections.singletonList(TransBizConstant.ACCOUNT_STATUS_1),
                            Arrays.asList(TransBizConstant.INTEREST_ACCRUAL_TYPE_10, TransBizConstant.INTEREST_ACCRUAL_TYPE_20), Collections.singletonList(planInfo.getBillingTenor()));
                    if (CollectionUtils.isNotEmpty(accruAcctList)) {
                        for (AmInterestAccrualInfo accuAcct : accruAcctList) {
                            pentyAmt = pentyAmt.add(accuAcct.getAccrualTotalAccruInterest());
                        }
                    }


                    if (TransBizConstant.LOAN_TYPE_4.equals(mainInfo.getLoanType())) {
                        penaltyAmount = penaltyAmount.add(pentyAmt);
                    }

                    if (Arrays.asList(TransBizConstant.LOAN_TYPE_2, TransBizConstant.LOAN_TYPE_3, TransBizConstant.LOAN_TYPE_7).contains(mainInfo.getLoanType())) {
                        delqStagingInterestAmount = delqStagingInterestAmount.add(pentyAmt);
                    }

                    lastBillBalance = lastBillBalance.add(planInfo.getTotalAmount()).subtract(planInfo.getRepayPrincipal()).subtract(planInfo.getRepayInterest());

                } else if (TransBizConstant.LOAN_PLAN_STATUS_1.equals(planInfo.getPlanStatus())) {
                    lastBillBalance = lastBillBalance.add(planInfo.getTotalAmount()).subtract(planInfo.getRepayInterest()).subtract(planInfo.getRepayPrincipal());
                } else if (TransBizConstant.LOAN_PLAN_STATUS_0.equals(planInfo.getPlanStatus())) {
                    lastBillBalance = lastBillBalance.add(planInfo.getTotalAmount());
                }
            }
        }

        res.setLastBillBalance(lastBillBalance);
        res.setCircleInterestAmount(circleInterestAmount);
        res.setPenaltyAmount(penaltyAmount);
        res.setDelqStagingInterestAmount(delqStagingInterestAmount);
        res.setBillTotalAmount(lastBillBalance.add(delqStagingInterestAmount)
                .add(penaltyAmount).add(circleInterestAmount));
        res.setTotalBalance(lastBillBalance.add(circleInterestAmount).add(penaltyAmount).add(delqStagingInterestAmount));
        if (res.getTotalBalance().compareTo(BigDecimal.ZERO) > 0 && queryPaymentDate.compareTo(bean.getPaymentDate()) == 0) {
            res.setMonthStatus(TransBizConstant.MONTH_STATUS_2);
        }


        //获取查询月的信息
        List<AmRepaymentPlanInfo> planInfoList = planList.stream().filter(plan -> plan.getPaymentDueDate().compareTo(queryPaymentDate) == 0).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(planInfoList)) {
            logger.info("未获取到当月还款计划信息");
            return res;
        }

        BigDecimal totalBalance = BigDecimal.ZERO;//剩余应还
        BigDecimal stagingAmount = BigDecimal.ZERO;//信用付欠款 判断是否账单分期
        BigDecimal paymentAmount = BigDecimal.ZERO;//还款金额
        BigDecimal circleInterestBalance = BigDecimal.ZERO;//剩余应还循环利息
        BigDecimal penaltyBalance = BigDecimal.ZERO;//剩余应还逾期罚息
        BigDecimal delqStagingInterestBalance = BigDecimal.ZERO;//剩余应还逾期分期利息
        BigDecimal stagingBillAmount = BigDecimal.ZERO;//分期入账金额



        Map<String, AmLoanAccountInfo> infoMap = loanAccountList.stream().collect(Collectors.toMap(AmLoanAccountInfo::getLoanId, Function.identity()));
        for (AmRepaymentPlanInfo planInfo : planInfoList) {
            BigDecimal thisPaymentAmt = BigDecimal.ZERO;
            BigDecimal thisRefundAmt = BigDecimal.ZERO;
            if (CollectionUtils.isNotEmpty(paymentLogList)) {
                thisPaymentAmt = paymentLogList.stream()
                        .filter(payment -> planInfo.getLoanId().equals(payment.getLoanId()))
                        .filter(payment -> !TransBizConstant.REPAYMENT_TYPE_REFUND.equals(payment.getPaymentType()))
                        .filter(payment -> !TransBizConstant.REPAYMENT_TYPE_STAGING.equals(payment.getPaymentType()))
                        .filter(payment -> planInfo.getBillingTenor().equals(payment.getBillingTenor()))
                        .map(TlPaymentAllocationLog::getPostingAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
                paymentAmount = paymentAmount.add(thisPaymentAmt);
                thisRefundAmt = paymentLogList.stream()
                        .filter(payment -> planInfo.getLoanId().equals(payment.getLoanId()))
                        .filter(payment -> TransBizConstant.REPAYMENT_TYPE_REFUND.equals(payment.getPaymentType()))
                        .filter(payment -> planInfo.getBillingTenor().equals(payment.getBillingTenor()))
                        .map(TlPaymentAllocationLog::getPostingAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
                refundAmount = refundAmount.add(thisRefundAmt);
            }

            //获取订单信息
            AmLoanAccountInfo loanAccountInfo = infoMap.get(planInfo.getLoanId());
            AmLoanMainInfo mainInfo = loanMainInfoList.stream().filter(loan -> planInfo.getLoanId().equals(loan.getLoanId())).findFirst().get();


            BigDecimal principalAmount = BigDecimal.ZERO;//本金
            BigDecimal interestAmount = BigDecimal.ZERO;//利息
            BigDecimal feeAmount = BigDecimal.ZERO;//费用
            BigDecimal curPenaltyAmount = BigDecimal.ZERO;
            BigDecimal compoundAmount = BigDecimal.ZERO;//复利
            BigDecimal billAmount = BigDecimal.ZERO;//订单当期金额
            boolean canTransStaging = true;//是否能交易分期
            if (TransBizConstant.LOAN_PLAN_STATUS_1.equals(planInfo.getPlanStatus())) {
                //计算欠款
                principalAmount = planInfo.getPrincipalAmount();
                feeAmount = planInfo.getFeeAmount();
                interestAmount = planInfo.getInterestAmount();
                billAmount = planInfo.getTotalAmount();
                totalBalance = totalBalance.add(planInfo.getTotalAmount()).subtract(planInfo.getRepayInterest()).subtract(planInfo.getRepayPrincipal());

            } else if (TransBizConstant.LOAN_PLAN_STATUS_0.equals(planInfo.getPlanStatus())) {
                //未到期取还款计划金额
                principalAmount = planInfo.getPrincipalAmount();
                feeAmount = planInfo.getFeeAmount();
                interestAmount = planInfo.getInterestAmount();
                billAmount = planInfo.getTotalAmount();
                totalBalance = totalBalance.add(planInfo.getTotalAmount());

            } else if (TransBizConstant.LOAN_PLAN_STATUS_2.equals(planInfo.getPlanStatus())) {
                canStaging = TransBizConstant.NO_FLAG;
                principalAmount = planInfo.getPrincipalAmount();
                feeAmount = planInfo.getFeeAmount();
                interestAmount = planInfo.getInterestAmount();

                DebtBean debtBean = debtCalculationService.getBalance(infoMap.get(planInfo.getLoanId()), Collections.singletonList(planInfo), bussinessDate, true, null, null, false);
                compoundAmount = debtBean.getCompountAmount();
                curPenaltyAmount = debtBean.getPenaltyAmount();

                billAmount = planInfo.getTotalAmount().add(compoundAmount).add(curPenaltyAmount)
                        .add(planInfo.getRepayPenaltyInterest()).add(planInfo.getRepayCompoundInterest());
                totalBalance = totalBalance.add(debtBean.getTotalAmount());

                if (TransBizConstant.LOAN_TYPE_4.equals(mainInfo.getLoanType())) {
                    penaltyBalance = penaltyBalance.add(curPenaltyAmount).add(compoundAmount);
                }

                if (Arrays.asList(TransBizConstant.LOAN_TYPE_2, TransBizConstant.LOAN_TYPE_3, TransBizConstant.LOAN_TYPE_7).contains(mainInfo.getLoanType())) {
                    delqStagingInterestBalance = delqStagingInterestBalance.add(curPenaltyAmount).add(compoundAmount);
                }
            } else {
                //已结清的获取还款计划 已还款部分金额
                principalAmount = planInfo.getRepayPrincipal();
                feeAmount = planInfo.getFeeAmount();
                interestAmount = planInfo.getRepayInterest();
                compoundAmount = planInfo.getRepayCompoundInterest();
                curPenaltyAmount = planInfo.getRepayPenaltyInterest();
                billAmount = principalAmount.add(interestAmount);
                canTransStaging = false;
            }

            if (Arrays.asList(TransBizConstant.LOAN_TYPE_2, TransBizConstant.LOAN_TYPE_3, TransBizConstant.LOAN_TYPE_7).contains(mainInfo.getLoanType())) {
                stagingBillAmount = stagingBillAmount.add(planInfo.getPrincipalAmount()).add(planInfo.getInterestAmount());
            }

            BillDetail billDetail = new BillDetail();
            billDetail.setLoanId(planInfo.getLoanId());
            billDetail.setTxnTime(mainInfo.getTxnTime());
            billDetail.setCreateTime(mainInfo.getCreateTime());
            billDetail.setMemo(mainInfo.getMemoid());//备注
            billDetail.setProductId(mainInfo.getProductId());
            billDetail.setProductType(mainInfo.getLoanType());//产品类型
            billDetail.setDelqDays(loanAccountInfo.getDelqDays());
            billDetail.setLoanAmount(mainInfo.getLoanAmount());
            billDetail.setBillAmount(billAmount);
            billDetail.setPrincipalAmount(principalAmount);//本金
            billDetail.setInterestAmount(interestAmount);//利息
            billDetail.setFeeAmount(feeAmount);//费用
            billDetail.setCompoundAmount(compoundAmount);//复利
            billDetail.setPenaltyAmount(curPenaltyAmount);//罚息
            billDetail.setTenor(mainInfo.getTotalTenor());//总期数
            billDetail.setBillingTenor(planInfo.getBillingTenor());//当前期数
            billDetail.setBillStatus(planInfo.getPlanStatus());
            billDetail.setRefundAmount(thisRefundAmt);

            //首期账单日 信用付的  没到首个账单日出账单的并且全未逾期的，可以交易分期  其他全不能
            Date firstStatementDate = this.getCurrentStatementDate(mainInfo.getFirstPaymentDate(), statementDd);
            if (TransBizConstant.LOAN_TYPE_4.equals(mainInfo.getLoanType()) && firstStatementDate.compareTo(bussinessDate) > 0
                    && canStaging.equals(TransBizConstant.YES_FLAG) && canTransStaging) {
                billDetail.setCanTransStaging(TransBizConstant.YES_FLAG);
            } else {
                billDetail.setCanTransStaging(TransBizConstant.NO_FLAG);
            }

            PmMerchantTable pmMerchantTable = extPmMerchantTableMapper.selectByTenantIdAndMerchantId(planInfo.getTenantId(), mainInfo.getMerchantId());
            if (null != pmMerchantTable) {
                billDetail.setMerchantId(mainInfo.getMerchantId());
                billDetail.setMerchantName(pmMerchantTable.getMerchantName());
            }
            //根据还款方式参数表ID查询还款方式信息
            PrAmortMethod amortMethod = amortMethodService.findByChargerOptionAndId(mainInfo.getAmortMethodTableId(), mainInfo.getOrganizationId(), tenantId);
            billDetail.setAmortMethod(amortMethod.getChargeOption());
            billDetails.add(billDetail);
        }

        //有欠款只显示欠款总金额
        res.setPaymentAmount(paymentAmount);
        res.setRefundAmount(refundAmount);
        res.setCircleInterestAmount(circleInterestBalance);
        res.setPenaltyBalance(penaltyBalance);
        res.setDelqStagingInterestBalance(delqStagingInterestBalance);
        res.setStagingBillAmount(stagingBillAmount);

        //获取上期账单日
        Date lastStatementDate = DateUtils.addMonth(queryStatementDate, -1);

        //账单分期金额
        BigDecimal billStagingAmount = loanMainInfoList.stream().filter(mainInfo -> mainInfo.getPostingDate().compareTo(queryStatementDate) >= 0)
                .filter(mainInfo -> mainInfo.getPostingDate().compareTo(queryPaymentDate) <= 0)
                .filter(mainInfo -> TransBizConstant.LOAN_TYPE_3.equals(mainInfo.getLoanType()))
                .map(AmLoanMainInfo::getLoanAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        //交易分期金额
        BigDecimal transStagingAmount = loanMainInfoList.stream().filter(mainInfo -> mainInfo.getPostingDate().compareTo(lastStatementDate) >= 0)
                .filter(mainInfo -> mainInfo.getPostingDate().compareTo(queryStatementDate) < 0)
                .filter(mainInfo -> TransBizConstant.LOAN_TYPE_7.equals(mainInfo.getLoanType()))
                .map(AmLoanMainInfo::getLoanAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        //本期新增消费
        BigDecimal consumeAmount = loanMainInfoList.stream().filter(mainInfo -> mainInfo.getPostingDate().compareTo(lastStatementDate) >= 0)
                .filter(mainInfo -> mainInfo.getPostingDate().compareTo(queryStatementDate) < 0)
                .filter(mainInfo -> TransBizConstant.LOAN_TYPE_4.equals(mainInfo.getLoanType()))
                .map(AmLoanMainInfo::getLoanAmount).reduce(BigDecimal.ZERO, BigDecimal::add);

        res.setTransStagingAmount(transStagingAmount);
        res.setBillStagingAmount(billStagingAmount);
        res.setConsumeAmount(consumeAmount);


        //5 是否允许账单分期判断
        if (bizTypeEnum != null) {
            if (bizTypeEnum.getCanStaging()) {
                if (canStaging.equals(TransBizConstant.YES_FLAG)) {
                    canStaging = checkLoanStaging(loanMainInfoList, queryStatementDate, queryPaymentDate, bussinessDate, stagingAmount);
                }
            } else {
                canStaging = TransBizConstant.NO_FLAG;
            }
        }

        //6 组装返回参数
        res.setCanStaging(canStaging);
        res.setBillDetailList(billDetails);

        UnaryOperator<String> getMonthStatus = monthStatus -> {
            if (TransBizConstant.LOAN_PLAN_STATUS_0.equals(monthStatus)) {
                return TransBizConstant.MONTH_STATUS_1;
            } else if (TransBizConstant.LOAN_PLAN_STATUS_1.equals(monthStatus)) {
                if (bean.getPaymentDate().compareTo(queryPaymentDate) == 0) {
                    return TransBizConstant.MONTH_STATUS_0;//当期
                } else {
                    return TransBizConstant.MONTH_STATUS_1;//未到期
                }
            } else if (TransBizConstant.LOAN_PLAN_STATUS_2.equals(monthStatus)) {
                return TransBizConstant.MONTH_STATUS_2;//逾期
            } else {
                return TransBizConstant.MONTH_STATUS_3;//结清
            }
        };

        String monthStatus = planInfoList.stream().filter(
                plan -> Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_0, TransBizConstant.LOAN_PLAN_STATUS_1, TransBizConstant.LOAN_PLAN_STATUS_2).contains(plan.getPlanStatus()))
                .map(AmRepaymentPlanInfo::getPlanStatus).max(String::compareTo).map(getMonthStatus).orElse(TransBizConstant.MONTH_STATUS_3);
        res.setMonthStatus(monthStatus);

        res.setTotalBalance(res.getTotalBalance().add(totalBalance));
        res.setBillTotalAmount(res.getLastBillBalance().add(res.getConsumeAmount()).add(res.getStagingBillAmount()).add(res.getCircleInterestAmount())
                .add(res.getPenaltyAmount()).add(res.getDelqStagingInterestAmount()));
        return res;
    }


    @Override
    public BillDetailsInquiryRes billWaitingDetails(BillDetailsInquiryReq req) throws Exception {
        BillDetailsInquiryRes res = new BillDetailsInquiryRes();
        res.setSeqNo(req.getSeqNo());

        List<BillDetail> billDetails = new ArrayList<>();

        String userId = req.getUserId();
        String channel = req.getChannel();
        String tenantId = req.getTenantId();
        String queryMonth = req.getQueryMonth();
        res.setBillMonth(queryMonth);


        //1 获取客户2级信息
        CmCustomerSecondLevel customer = customerService.getSecondLevelByCondition(userId,
                channel, tenantId, null);
        if (customer == null) {
            logger.error("客户信息未找到，用户编号：{}，渠道：{}", userId, channel);
            throw new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
        }
        int paymentDd = customer.getPaymentDd();
        Integer statementDd = customer.getStatementDd();
        if (statementDd == null || statementDd == 0) {
            logger.info("客户还款日账单日不存在");
            res.setCanStaging(TransBizConstant.NO_FLAG);
            res.setHasDelq(TransBizConstant.NO_FLAG);
            res.setBillTotalAmount(BigDecimal.ZERO);
            return res;
        }

        //2 获取业务日期
        PrOrganizationTable organization = organizationTableService.findByOrId(customer.getOrganizationId(), tenantId);
        Date businessDate = organization.getNextProcessingDate();


        //2 获取查询月份账单日对应的还款日
        List<String> products;
        BizTypeEnum bizTypeEnum = null;
        if (StringUtils.isNotBlank(req.getBizType())) {
            bizTypeEnum = BizTypeEnum.getByCode(req.getBizType());
            products = BizTypeProducts.getByBizType(BizTypeEnum.getByCode(req.getBizType()));
        } else {
            products = req.getProductIds();
        }
        String productId = products.get(0);
        CurrentStatementBean bean = this.getCurrentStatement(productId, statementDd, paymentDd, businessDate);
        paymentDd = DateUtils.getDayOfMonth(bean.getPaymentDate());
        statementDd = DateUtils.getDayOfMonth(bean.getStatementDate());

        Date queryStatementDate = DateUtils.getThisMonthDate(queryMonth, DateUtils.YEAR_MONTH_PATTERN, statementDd);
        Date queryPaymentDate = this.getCurrentPaymentDate(queryStatementDate, paymentDd);
        Date lastQueryStatementDate = DateUtils.addMonth(queryStatementDate, -1);

        res.setPaymentDate(queryPaymentDate);//月还款日
        res.setCanStaging(TransBizConstant.NO_FLAG);
        if (bean.getPaymentDate().compareTo(queryPaymentDate) == 0) {
            res.setMonthStatus(TransBizConstant.MONTH_STATUS_0);
        } else {
            res.setMonthStatus(TransBizConstant.MONTH_STATUS_1);
        }

        //获取退款溢缴款信息
        BigDecimal refundAmount = BigDecimal.ZERO;//退款金额
        List<TlRefundLog> tlRefundLogList = extTlRefundLogMapper.selectByCustomerIdProductIdsAndTenantId(customer.getCustomerId(), customer.getTenantId(), products);
        if (CollectionUtils.isNotEmpty(tlRefundLogList)) {
            refundAmount = tlRefundLogList.stream().filter(log -> log.getBusinessDate().compareTo(queryStatementDate) >= 0)
                    .filter(log -> log.getBusinessDate().compareTo(queryPaymentDate) <= 0).map(TlRefundLog::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        }
        res.setRefundAmount(refundAmount);


        BigDecimal queryMonthPaymentAmount = BigDecimal.ZERO;//当月还款金额
        BigDecimal queryMonthRefundAmount = BigDecimal.ZERO;//当月退款金额
        List<TlPaymentAllocationLog> paymentLogList = extTlPaymentAllocationLogMapper.selectAll09WithProducts(customer.getCustomerId(), customer.getTenantId(), TransBizConstant.ACCOUNT_TYPE_09, products);
        if (CollectionUtils.isNotEmpty(paymentLogList)) {
            queryMonthPaymentAmount = queryMonthPaymentAmount.add(paymentLogList.stream().filter(payment -> !TransBizConstant.REPAYMENT_TYPE_REFUND.equals(payment.getPaymentType()))
                    .filter(payment -> !TransBizConstant.REPAYMENT_TYPE_STAGING.equals(payment.getPaymentType()))
                    .filter(payment -> payment.getPostingDate().compareTo(lastQueryStatementDate) >= 0)
                    .filter(payment -> payment.getPostingDate().compareTo(queryStatementDate) < 0)
                    .map(TlPaymentAllocationLog::getPostingAmount).reduce(BigDecimal.ZERO, BigDecimal::add));
            queryMonthRefundAmount = queryMonthRefundAmount.add(paymentLogList.stream().filter(payment -> TransBizConstant.REPAYMENT_TYPE_REFUND.equals(payment.getPaymentType()))
                    .filter(payment -> payment.getPostingDate().compareTo(lastQueryStatementDate) >= 0)
                    .filter(payment -> payment.getPostingDate().compareTo(queryStatementDate) < 0)
                    .map(TlPaymentAllocationLog::getPostingAmount).reduce(BigDecimal.ZERO, BigDecimal::add));
        }
        res.setQueryMonthPaymentAmount(queryMonthPaymentAmount);
        res.setQueryMonthRefundAmount(queryMonthRefundAmount);


        List<AmLoanAccountInfo> loanAccountList = extAmLoanAccountInfoMapper.selectByCustomerIdAndLoanTypeProductIdsAndNotStatus(req.getTenantId(),
                customer.getCustomerId(), channel, Collections.singletonList(TransBizConstant.ACCOUNT_STATUS_9),
                Arrays.asList(TransBizConstant.LOAN_TYPE_2, TransBizConstant.LOAN_TYPE_3, TransBizConstant.LOAN_TYPE_4, TransBizConstant.LOAN_TYPE_5, TransBizConstant.LOAN_TYPE_7), products);
        if (CollectionUtils.isEmpty(loanAccountList)) {
            logger.info("未获取到订单账户信息，客户号：{}", customer.getCustomerId());
            return res;
        }

        String hasDelq = TransBizConstant.NO_FLAG;
        String canStaging = TransBizConstant.YES_FLAG;

        //判断客户是否有逾期
        if (loanAccountList.stream().anyMatch(loan -> TransBizConstant.LOAN_STATUS_2.equals(loan.getAccountStatus()))) {
            hasDelq = TransBizConstant.YES_FLAG;
            canStaging = TransBizConstant.NO_FLAG;
        }
        res.setHasDelq(hasDelq);


        //4 根据订单 还款日期 查询还款计划
        List<String> loanIdList = new ArrayList<>();
        for (AmLoanAccountInfo loanAccountInfo : loanAccountList) {
            loanIdList.add(loanAccountInfo.getLoanId());
        }

        List<AmLoanMainInfo> loanMainInfoList = extAmLoanMainInfoMapper.selectByCusIdtProductIdsAndTenantId(customer.getCustomerId(),
                req.getTenantId(), products);
        loanMainInfoList = loanMainInfoList.stream().filter(loan -> loanIdList.contains(loan.getLoanId())).collect(Collectors.toList());

        List<AmRepaymentPlanInfo> planList =
                extAmRepaymentPlanInfoMapper.selectByLoanIdListAndStatus(
                        loanIdList, customer.getCustomerId(), customer.getTenantId(),
                        Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_1, TransBizConstant.LOAN_PLAN_STATUS_2, TransBizConstant.LOAN_PLAN_STATUS_0
                                , TransBizConstant.LOAN_PLAN_STATUS_6, TransBizConstant.LOAN_PLAN_STATUS_7, TransBizConstant.LOAN_PLAN_STATUS_8));
        if (CollectionUtils.isEmpty(planList)) {
            logger.info("未获取到所有还款计划信息");
            return res;
        }

        //获取历史数据
        BigDecimal lastBillBalance = BigDecimal.ZERO;//上月账单余额
        BigDecimal circleInterestAmount = BigDecimal.ZERO;//循环利息
        BigDecimal penaltyAmount = BigDecimal.ZERO;//逾期罚息
        BigDecimal delqStagingInterestAmount = BigDecimal.ZERO;//上月账单罚息、复利估算到本期还款日


        List<AmRepaymentPlanInfo> historyPlanInfoList = planList.stream().filter(plan -> plan.getPaymentDueDate().compareTo(queryPaymentDate) < 0).collect(Collectors.toList());

        if (CollectionUtils.isNotEmpty(historyPlanInfoList)) {
            for (AmRepaymentPlanInfo planInfo : historyPlanInfoList) {
                //获取订单信息
                AmLoanMainInfo mainInfo = loanMainInfoList.stream().filter(loan -> planInfo.getLoanId().equals(loan.getLoanId())).findFirst().get();
                if (TransBizConstant.LOAN_PLAN_STATUS_2.equals(planInfo.getPlanStatus())) {
                    BigDecimal pentyAmt = BigDecimal.ZERO;
                    List<AmInterestAccrualInfo> accruAcctList = extAmInterestAccrualInfoMapper.selectByStatusAndType(
                            planInfo.getLoanId(), planInfo.getCustomerId(), tenantId, Collections.singletonList(TransBizConstant.ACCOUNT_STATUS_1),
                            Arrays.asList(TransBizConstant.INTEREST_ACCRUAL_TYPE_10, TransBizConstant.INTEREST_ACCRUAL_TYPE_20), Collections.singletonList(planInfo.getBillingTenor()));
                    if (CollectionUtils.isNotEmpty(accruAcctList)) {
                        for (AmInterestAccrualInfo accuAcct : accruAcctList) {
                            pentyAmt = pentyAmt.add(accuAcct.getAccrualTotalAccruInterest());
                        }
                    }
                    if (TransBizConstant.LOAN_TYPE_4.equals(mainInfo.getLoanType())) {
                        penaltyAmount = penaltyAmount.add(pentyAmt);
                    }

                    if (Arrays.asList(TransBizConstant.LOAN_TYPE_2, TransBizConstant.LOAN_TYPE_3, TransBizConstant.LOAN_TYPE_7).contains(mainInfo.getLoanType())) {
                        delqStagingInterestAmount = delqStagingInterestAmount.add(pentyAmt);
                    }

                    lastBillBalance = lastBillBalance.add(planInfo.getTotalAmount()).subtract(planInfo.getRepayPrincipal()).subtract(planInfo.getRepayInterest());

                } else if (TransBizConstant.LOAN_PLAN_STATUS_1.equals(planInfo.getPlanStatus())) {
                    lastBillBalance = lastBillBalance.add(planInfo.getTotalAmount()).subtract(planInfo.getRepayInterest()).subtract(planInfo.getRepayPrincipal());
                } else if (TransBizConstant.LOAN_PLAN_STATUS_0.equals(planInfo.getPlanStatus())) {
                    lastBillBalance = lastBillBalance.add(planInfo.getTotalAmount());
                }
            }
        }

        res.setLastBillBalance(lastBillBalance);
        res.setPenaltyAmount(penaltyAmount);
        res.setDelqStagingInterestAmount(delqStagingInterestAmount);
        res.setCircleInterestAmount(circleInterestAmount);

        //获取<=查询月的信息
        List<AmRepaymentPlanInfo> planInfoList = planList.stream().filter(plan -> plan.getPaymentDueDate().compareTo(queryPaymentDate) <= 0).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(planInfoList)) {
            logger.info("未获取到当月还款计划信息");
            return res;
        }

        BigDecimal totalBalance = BigDecimal.ZERO;//剩余应还
        BigDecimal stagingAmount = BigDecimal.ZERO;//信用付欠款 判断是否账单分期
        BigDecimal paymentAmount = BigDecimal.ZERO;//还款金额
        BigDecimal circleInterestBalance = BigDecimal.ZERO;//剩余应还循环利息
        BigDecimal penaltyBalance = BigDecimal.ZERO;//剩余应还逾期罚息
        BigDecimal delqStagingInterestBalance = BigDecimal.ZERO;//剩余应还逾期分期利息
        BigDecimal stagingBillAmount = BigDecimal.ZERO;//分期入账金额


        Map<String, AmLoanAccountInfo> infoMap = loanAccountList.stream().collect(Collectors.toMap(AmLoanAccountInfo::getLoanId, Function.identity()));
        for (AmRepaymentPlanInfo planInfo : planInfoList) {
            BigDecimal thisPaymentAmt;
            BigDecimal thisRefundAmt = BigDecimal.ZERO;
            if (CollectionUtils.isNotEmpty(paymentLogList)) {
                thisPaymentAmt = paymentLogList.stream()
                        .filter(payment -> planInfo.getLoanId().equals(payment.getLoanId()))
                        .filter(payment -> !TransBizConstant.REPAYMENT_TYPE_REFUND.equals(payment.getPaymentType()))
                        .filter(payment -> !TransBizConstant.REPAYMENT_TYPE_STAGING.equals(payment.getPaymentType()))
                        .filter(payment -> planInfo.getBillingTenor().equals(payment.getBillingTenor()))
                        .map(TlPaymentAllocationLog::getPostingAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
                paymentAmount = paymentAmount.add(thisPaymentAmt);
                thisRefundAmt = paymentLogList.stream()
                        .filter(payment -> planInfo.getLoanId().equals(payment.getLoanId()))
                        .filter(payment -> TransBizConstant.REPAYMENT_TYPE_REFUND.equals(payment.getPaymentType()))
                        .filter(payment -> planInfo.getBillingTenor().equals(payment.getBillingTenor()))
                        .map(TlPaymentAllocationLog::getPostingAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
                refundAmount = refundAmount.add(thisRefundAmt);
            }

            //获取订单信息
            AmLoanAccountInfo loanAccountInfo = infoMap.get(planInfo.getLoanId());
            AmLoanMainInfo mainInfo = loanMainInfoList.stream().filter(loan -> planInfo.getLoanId().equals(loan.getLoanId())).findFirst().get();


            BigDecimal principalAmount = BigDecimal.ZERO;//本金
            BigDecimal interestAmount = BigDecimal.ZERO;//利息
            BigDecimal feeAmount = BigDecimal.ZERO;//费用
            BigDecimal curPenaltyAmount = BigDecimal.ZERO;
            BigDecimal compoundAmount = BigDecimal.ZERO;//复利
            BigDecimal billAmount = BigDecimal.ZERO;//订单当期金额
            boolean canTransStaging = true;//是否能交易分期
            if (TransBizConstant.LOAN_PLAN_STATUS_1.equals(planInfo.getPlanStatus())) {
                //计算欠款
                principalAmount = planInfo.getPrincipalAmount();
                feeAmount = planInfo.getFeeAmount();
                interestAmount = planInfo.getInterestAmount();
                billAmount = planInfo.getTotalAmount();
                // 兴安借/付产品待还账单金额处理
                if (Arrays.asList(BizTypeProducts.XAJ_PRODUCT, BizTypeProducts.PRODUCT_XAF).contains(planInfo.getProductId())) {
                    DebtDetailBean detailBean = this.getXajBalance(planInfo);
                    principalAmount = detailBean.getPrincipalAmount();
                    feeAmount = detailBean.getFeeAmount();
                    interestAmount = detailBean.getInterestAmount();
                    billAmount = principalAmount.add(feeAmount).add(interestAmount);
                    totalBalance = totalBalance.add(billAmount);
                } else {
                    totalBalance = totalBalance.add(planInfo.getTotalAmount()).subtract(planInfo.getRepayInterest()).subtract(planInfo.getRepayPrincipal());
                }
            } else if (TransBizConstant.LOAN_PLAN_STATUS_0.equals(planInfo.getPlanStatus())) {
                //未到期取还款计划金额
                principalAmount = planInfo.getPrincipalAmount();
                feeAmount = planInfo.getFeeAmount();
                interestAmount = planInfo.getInterestAmount();
                billAmount = planInfo.getTotalAmount();
                totalBalance = totalBalance.add(planInfo.getTotalAmount());
            } else if (TransBizConstant.LOAN_PLAN_STATUS_2.equals(planInfo.getPlanStatus())) {
                canStaging = TransBizConstant.NO_FLAG;
                principalAmount = planInfo.getPrincipalAmount();
                feeAmount = planInfo.getFeeAmount();
                interestAmount = planInfo.getInterestAmount();

                DebtBean debtBean = debtCalculationService.getBalance(infoMap.get(planInfo.getLoanId()), Collections.singletonList(planInfo), businessDate, true, null, null, false);
                compoundAmount = debtBean.getCompountAmount();
                curPenaltyAmount = debtBean.getPenaltyAmount();

                billAmount = planInfo.getTotalAmount().add(compoundAmount).add(curPenaltyAmount)
                        .add(planInfo.getRepayPenaltyInterest()).add(planInfo.getRepayCompoundInterest());
                // 兴安借/付产品待还账单金额处理
                if (Arrays.asList(BizTypeProducts.XAJ_PRODUCT, BizTypeProducts.PRODUCT_XAF).contains(planInfo.getProductId())) {
                    DebtDetailBean detailBean = this.getXajBalance(planInfo);
                    principalAmount = detailBean.getPrincipalAmount();
                    feeAmount = detailBean.getFeeAmount();
                    interestAmount = detailBean.getInterestAmount();
                    billAmount = principalAmount.add(feeAmount).add(interestAmount).add(compoundAmount).add(curPenaltyAmount);
                    totalBalance = totalBalance.add(billAmount);
                } else {
                    totalBalance = totalBalance.add(debtBean.getTotalAmount());
                }

                if (TransBizConstant.LOAN_TYPE_4.equals(mainInfo.getLoanType())) {
                    penaltyBalance = penaltyBalance.add(curPenaltyAmount).add(compoundAmount);
                }

                if (Arrays.asList(TransBizConstant.LOAN_TYPE_2, TransBizConstant.LOAN_TYPE_3, TransBizConstant.LOAN_TYPE_7).contains(mainInfo.getLoanType())) {
                    delqStagingInterestBalance = delqStagingInterestBalance.add(curPenaltyAmount).add(compoundAmount);
                }
            } else {
                //已结清的获取还款计划 已还款部分金额
                principalAmount = planInfo.getRepayPrincipal();
                feeAmount = planInfo.getFeeAmount();
                interestAmount = planInfo.getRepayInterest();
                compoundAmount = planInfo.getRepayCompoundInterest();
                curPenaltyAmount = planInfo.getRepayPenaltyInterest();
                billAmount = principalAmount.add(interestAmount);
                canTransStaging = false;
            }

            if (Arrays.asList(TransBizConstant.LOAN_TYPE_2, TransBizConstant.LOAN_TYPE_3, TransBizConstant.LOAN_TYPE_7).contains(mainInfo.getLoanType())) {
                stagingBillAmount = stagingBillAmount.add(planInfo.getPrincipalAmount()).add(planInfo.getInterestAmount());
            }

            BillDetail billDetail = new BillDetail();
            billDetail.setLoanId(planInfo.getLoanId());
            billDetail.setTxnTime(mainInfo.getTxnTime());
            billDetail.setCreateTime(mainInfo.getCreateTime());
            billDetail.setMemo(mainInfo.getMemoid());//备注
            billDetail.setProductId(mainInfo.getProductId());
            billDetail.setProductType(mainInfo.getLoanType());//产品类型
            billDetail.setDelqDays(loanAccountInfo.getDelqDays());
            billDetail.setLoanAmount(mainInfo.getLoanAmount());
            billDetail.setBillAmount(billAmount);
            billDetail.setPrincipalAmount(principalAmount);//本金
            billDetail.setInterestAmount(interestAmount);//利息
            billDetail.setFeeAmount(feeAmount);//费用
            billDetail.setCompoundAmount(compoundAmount);//复利
            billDetail.setPenaltyAmount(curPenaltyAmount);//罚息
            billDetail.setTenor(mainInfo.getTotalTenor());//总期数
            billDetail.setBillingTenor(planInfo.getBillingTenor());//当前期数
            billDetail.setBillStatus(planInfo.getPlanStatus());
            billDetail.setRefundAmount(thisRefundAmt);

            //首期账单日 信用付的  没到首个账单日出账单的并且全未逾期的，可以交易分期  其他全不能
            Date firstStatementDate = this.getCurrentStatementDate(mainInfo.getFirstPaymentDate(), statementDd);
            if (TransBizConstant.LOAN_TYPE_4.equals(mainInfo.getLoanType()) && firstStatementDate.compareTo(businessDate) > 0
                    && canStaging.equals(TransBizConstant.YES_FLAG) && canTransStaging) {
                billDetail.setCanTransStaging(TransBizConstant.YES_FLAG);
            } else {
                billDetail.setCanTransStaging(TransBizConstant.NO_FLAG);
            }

            PmMerchantTable pmMerchantTable = extPmMerchantTableMapper.selectByTenantIdAndMerchantId(planInfo.getTenantId(), mainInfo.getMerchantId());
            if (null != pmMerchantTable) {
                billDetail.setMerchantId(mainInfo.getMerchantId());
                billDetail.setMerchantName(pmMerchantTable.getMerchantName());
            }
            //根据还款方式参数表ID查询还款方式信息
            PrAmortMethod amortMethod = amortMethodService.findByChargerOptionAndId(mainInfo.getAmortMethodTableId(), mainInfo.getOrganizationId(), tenantId);
            billDetail.setAmortMethod(amortMethod.getChargeOption());
            billDetails.add(billDetail);
        }

        //有欠款只显示欠款总金额
        res.setPaymentAmount(paymentAmount);
        res.setRefundAmount(refundAmount);
        res.setCircleInterestAmount(circleInterestBalance);
        res.setPenaltyBalance(penaltyBalance);
        res.setDelqStagingInterestBalance(delqStagingInterestBalance);
        res.setStagingBillAmount(stagingBillAmount);

        //获取上期账单日
        Date lastStatementDate = DateUtils.addMonth(queryStatementDate, -1);

        //账单分期金额
        BigDecimal billStagingAmount = loanMainInfoList.stream().filter(mainInfo -> mainInfo.getPostingDate().compareTo(queryStatementDate) >= 0)
                .filter(mainInfo -> mainInfo.getPostingDate().compareTo(queryPaymentDate) <= 0)
                .filter(mainInfo -> TransBizConstant.LOAN_TYPE_3.equals(mainInfo.getLoanType()))
                .map(AmLoanMainInfo::getLoanAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        //交易分期金额
        BigDecimal transStagingAmount = loanMainInfoList.stream().filter(mainInfo -> mainInfo.getPostingDate().compareTo(lastStatementDate) >= 0)
                .filter(mainInfo -> mainInfo.getPostingDate().compareTo(queryStatementDate) < 0)
                .filter(mainInfo -> TransBizConstant.LOAN_TYPE_7.equals(mainInfo.getLoanType()))
                .map(AmLoanMainInfo::getLoanAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        //消费分期金额
        BigDecimal consumeStagingAmount = loanMainInfoList.stream().filter(mainInfo -> mainInfo.getPostingDate().compareTo(lastStatementDate) >= 0)
                .filter(mainInfo -> mainInfo.getPostingDate().compareTo(queryStatementDate) < 0)
                .filter(mainInfo -> TransBizConstant.LOAN_TYPE_2.equals(mainInfo.getLoanType()))
                .map(AmLoanMainInfo::getLoanAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        //本期新增消费
        BigDecimal consumeAmount = loanMainInfoList.stream().filter(mainInfo -> mainInfo.getPostingDate().compareTo(lastStatementDate) >= 0)
                .filter(mainInfo -> mainInfo.getPostingDate().compareTo(queryStatementDate) < 0)
                .filter(mainInfo -> TransBizConstant.LOAN_TYPE_4.equals(mainInfo.getLoanType()))
                .map(AmLoanMainInfo::getLoanAmount).reduce(BigDecimal.ZERO, BigDecimal::add);

        res.setConsumeStagingAmount(consumeStagingAmount);
        res.setTransStagingAmount(transStagingAmount);
        res.setBillStagingAmount(billStagingAmount);
        res.setConsumeAmount(consumeAmount);


        //5 是否允许账单分期判断
        if (bizTypeEnum != null) {
            if (bizTypeEnum.getCanStaging()) {
                if (canStaging.equals(TransBizConstant.YES_FLAG)) {
                    canStaging = checkLoanStaging(loanMainInfoList, queryStatementDate, queryPaymentDate, businessDate, stagingAmount);
                }
            } else {
                canStaging = TransBizConstant.NO_FLAG;
            }
        }

        //6 组装返回参数
        res.setCanStaging(canStaging);
        res.setBillDetailList(billDetails);

        UnaryOperator<String> getMonthStatus = monthStatus -> {
            if (TransBizConstant.LOAN_PLAN_STATUS_0.equals(monthStatus)) {
                return TransBizConstant.MONTH_STATUS_1;
            } else if (TransBizConstant.LOAN_PLAN_STATUS_1.equals(monthStatus)) {
                if (bean.getPaymentDate().compareTo(queryPaymentDate) == 0) {
                    return TransBizConstant.MONTH_STATUS_0;//当期
                } else {
                    return TransBizConstant.MONTH_STATUS_1;//未到期
                }
            } else if (TransBizConstant.LOAN_PLAN_STATUS_2.equals(monthStatus)) {
                return TransBizConstant.MONTH_STATUS_2;//逾期
            } else {
                return TransBizConstant.MONTH_STATUS_3;//结清
            }
        };

        String monthStatus = planInfoList.stream().filter(
                plan -> Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_0, TransBizConstant.LOAN_PLAN_STATUS_1, TransBizConstant.LOAN_PLAN_STATUS_2).contains(plan.getPlanStatus()))
                .map(AmRepaymentPlanInfo::getPlanStatus).max(String::compareTo).map(getMonthStatus).orElse(TransBizConstant.MONTH_STATUS_3);
        res.setMonthStatus(monthStatus);

        res.setTotalBalance(res.getTotalBalance().add(totalBalance));
        res.setBillTotalAmount(res.getLastBillBalance().add(res.getConsumeAmount()).add(res.getStagingBillAmount()).add(res.getCircleInterestAmount())
                .add(res.getPenaltyAmount()).add(res.getDelqStagingInterestAmount()));
        return res;
    }

    /**
     * 账单分期特殊处理首期还款日
     * @param loanParamBean
     * @param loanReq
     */
    private void verifyFirstPaymentDate(LoanParamBean loanParamBean,LoanReq loanReq) {
        if(null != loanReq.getPaymentDate()){
            loanParamBean.setFirstPaymentDate(loanReq.getPaymentDate());
        }
    }

    /**
     * 借款记录查询
     *
     * @param req
     * @return
     */
    @Override
    public LoanListQueryRes loanListQuery(LoanListQueryReq req) throws TxnBizException {
        return loanService.loanListQuery(req);
    }

    /**
     * 通知api-server放款状态更新
     *
     * @param log
     * @param completeDate
     * @param contractNumber
     */
    @Override
    public void disbursementStatus(TlDisbursementLog log, Date completeDate, String contractNumber) throws TxnBizException {
        CmCustomerSecondLevel second = customerService.getSecondByChannelAndCustomerIdAndTensnId(log.getChannel(), log.getCustomerId(), log.getTenantId(), null);
        // 通知api放款结果
        LoanResultCallBackReq req = new LoanResultCallBackReq();
        req.setDisbursementStatus(this.getDisbursementStatus(log.getSumStatus()));
        req.setProductId(log.getProductId());
        req.setContractNumber(contractNumber);
        req.setSeqNo(log.getSeqNo());
        req.setLoanId(log.getLoanId());
        req.setReturnMessage(log.getReturnMessage());
        req.setLoanDate(DateUtils.format(completeDate));
        req.setReqSeq(log.getSeqNo());
        req.setUserId(second.getUserId());
        req.setTenantId(log.getTenantId());
        req.setChannelNo(log.getChannel());
        iUserApiService.loanResultCallBack(JSON.toJSONString(req));
    }

    /**
     * 转换放款状态
     * @param sumStatus
     * @return
     */
    public String getDisbursementStatus(String sumStatus){
        String disbursementStatus;
        if (TransBizConstant.DISBURSEMENT_LOG_SUM_STATUS_S.equals(sumStatus)) {
            // 放款成功
            disbursementStatus = TransBizConstant.DISBURSEMENT_STATUS_SUCCESS;
        } else if (TransBizConstant.DISBURSEMENT_LOG_SUM_STATUS_F.equalsIgnoreCase(sumStatus)) {
            // 放款失败
            disbursementStatus = TransBizConstant.DISBURSEMENT_STATUS_ERROR;
        } else if (TransBizConstant.DISBURSEMENT_LOG_SUM_STATUS_O.equals(sumStatus)) {
            // 放款中
            disbursementStatus = TransBizConstant.DISBURSEMENT_STATUS_AWAIT;
        } else {
            // 未知
            disbursementStatus = TransBizConstant.DISBURSEMENT_STATUS_UNKNOWN;
        }
        return disbursementStatus;
    }

    /**
     * PCP放款结果回调处理
     * @param req
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public LoanCallBackRes loanCallBackHandle(LoanCallBackRep req) throws Exception {
        LoanCallBackRes res = new LoanCallBackRes();
        res.setSeqNo(req.getSeqNo());
        TlDisbursementLog tlDisbursementLog = extTlDisbursementLogMapper.selectBySeqNoAndTenantId(req.getSeqNo(), req.getTenantId());

        // 3 业务日
        PrOrganizationTable organization = organizationTableService.findByOrId(tlDisbursementLog.getOrganizationId(), tlDisbursementLog.getTenantId());
        Date businessDate = organization.getNextProcessingDate();

        // 放款流水不存在，抛出异常
        if(ObjectUtils.isEmpty(tlDisbursementLog)){
            res.setValueDate(businessDate);
            res.setSunStatus(TransBizConstant.GATEWAY_DEAL_FAIL);
            return res;
        }

        // 放款流水存在且放款最终状态为处理中
        if (!ObjectUtils.isEmpty(tlDisbursementLog) &&
                TransBizConstant.DISBURSEMENT_LOG_SUM_STATUS_O.equals(tlDisbursementLog.getSumStatus())){
            TlDisbursementLog updateLog = new TlDisbursementLog();
            updateLog.setId(tlDisbursementLog.getId());
            this.getDisSumStatus(req.getOperStatus(), updateLog);
            updateLog.setUpdateBy(TransBizConstant.SYS_OPRATE);
            updateLog.setRecordVersionNumber(TransBizConstant.INIT_RECORD_VERSION_NUMBER);
            updateLog.setTenantId(tlDisbursementLog.getTenantId());
            updateLog.setCustomerId(tlDisbursementLog.getCustomerId());
            extTlDisbursementLogMapper.updateLog(updateLog);
            LoanSaveBean loanSaveBean = new LoanSaveBean();
            this.dealLoanResult(tlDisbursementLog, loanSaveBean, businessDate, updateLog, updateLog.getSumStatus());
            this.saveOrUpdateLoan(Collections.singletonList(loanSaveBean));
        }
        res.setValueDate(businessDate);
        res.setSunStatus(TransBizConstant.GATEWAY_DEAL_SUC);
        return res;
    }

    /**
     * 行内交易状态转换放款最终状态
     * @param operStatus 放款回调 行内交易状态  00-待处理，01-已记账，02-已抹账，03-退汇完成
     * @param updateLog
     * @return
     */
    @Override
    public void getDisSumStatus(String operStatus, TlDisbursementLog updateLog) {
        // 设置默认放款最终状态 O-已发起放款，放款状态未知，待查询
        String sumStatus = TransBizConstant.DISBURSEMENT_LOG_SUM_STATUS_O;
        String returnMessage = "";

        // 放款结果回调状态 (00-待处理)：O-已发起放款，放款状态未知，待查询
        if ("00".equals(operStatus)) {
            sumStatus = TransBizConstant.DISBURSEMENT_LOG_SUM_STATUS_O;
            returnMessage = "00-待处理";
        }
        // 放款结果回调状态 (01-已记账)：S-放款成功
        if ("01".equals(operStatus)) {
            sumStatus = TransBizConstant.DISBURSEMENT_LOG_SUM_STATUS_S;
            returnMessage = "01-已记账";
        }
        // 放款结果回调状态 (02-已抹账，03-退汇完成)：F-放款失败
        if ("02".equals(operStatus)) {
            sumStatus = TransBizConstant.DISBURSEMENT_LOG_SUM_STATUS_F;
            returnMessage = "02-已抹账";
        }
        // 放款结果回调状态 (02-已抹账，03-退汇完成)：F-放款失败
        if ("03".equals(operStatus)) {
            sumStatus = TransBizConstant.DISBURSEMENT_LOG_SUM_STATUS_F;
            returnMessage = "03-退汇完成";
        }
        updateLog.setSumStatus(sumStatus);
        updateLog.setReturnStatus(sumStatus);
        updateLog.setReturnMessage(returnMessage);
    }

    /**
     * 处理放款结果
     * @param oldLog
     * @param loanSaveBean
     * @param businessDate
     * @param updateLog
     * @param result
     * @return
     * @throws TxnException
     */
    @Override
    public void dealLoanResult(TlDisbursementLog oldLog, LoanSaveBean loanSaveBean, Date businessDate, TlDisbursementLog updateLog, String result) throws Exception {
        AmLoanMainInfo amLoanMainInfo = extAmLoanMainInfoMapper.selectByLoanIdAndCusId(oldLog.getLoanId(), oldLog.getCustomerId(), oldLog.getTenantId());
        if (TransBizConstant.GATEWAY_DEAL_SUC.equals(result)) { //成功
            updateLog.setSumStatus(TransBizConstant.DISBURSEMENT_LOG_SUM_STATUS_S);
            updateLog.setCollateDate(businessDate);
            AmLoanAccountInfo amLoanAccountInfo = extAmLoanAccountInfoMapper.selectByLoanId(oldLog.getLoanId(), oldLog.getCustomerId(), oldLog.getTenantId());

            //放款成功记录五级分类变动
            TlLoanClassifyChange tlLoanClassifyChange = tlLoanClassifyService.buildLoanClassifyChange(amLoanAccountInfo, businessDate,amLoanMainInfo.getLoanAmount(),TransBizConstant.LOAN_CLASSIFY_0);
            loanSaveBean.setTlLoanClassifyChange(tlLoanClassifyChange);

            AfterSucSaveBean afterSucSaveBean = loanService.afterLoanSuccess(amLoanMainInfo,amLoanAccountInfo, businessDate);
            loanSaveBean.setAfterSucSaveBean(afterSucSaveBean);

        } else if (TransBizConstant.GATEWAY_DEAL_FAIL.equals(result)) {
            //失败
            updateLog.setSumStatus(TransBizConstant.DISBURSEMENT_LOG_SUM_STATUS_F);
            updateLog.setCollateDate(businessDate);
            updateLog.setCancelStatus(TransBizConstant.NO_FLAG);
            if(TransBizConstant.YES_FLAG.equals(oldLog.getJointLoanFlag())) {
                // 如果是联合贷订单则撤销资金源订单撤销
                AmLoanMainInfo loanMainInfo = loanMainInfoService.getAmLoanMainInfoByLoanIdAndCusId(oldLog.getLoanId(), oldLog.getCustomerId(), oldLog.getTenantId());
                InvestorCancelLoanReq req = new InvestorCancelLoanReq(oldLog.getLoanId(), oldLog.getCustomerId(), oldLog.getTenantId(), oldLog.getSeqNo(), oldLog.getChannel(), loanMainInfo.getFundId(), oldLog.getProductId());
                anyTxnInvestorService.cancelLoan(req);
            }
            //放款失败合作方额度恢复
            partnerLimitRecoveryService.partnerLimitRecovery(oldLog.getProductId(), oldLog.getOrganizationId(),
                    oldLog.getTenantId(), oldLog.getAmount(), oldLog.getSeqNo());
        }
        // 兴安借产品放款结果查询处理
        this.queryLoanStatusByXaj(updateLog, oldLog.getProductId(), oldLog.getQueryMaxCounts());
        loanSaveBean.setUpdateLog(updateLog);

        //只有拿到终态的才需要发送短信
        if(!TransBizConstant.DISBURSEMENT_LOG_SUM_STATUS_O.equals(updateLog.getSumStatus())){
            //兴安付发送受托支付通知短信所做的兼容处理
            //if (Constants.XAF_PRODUCT_CODE.equals(oldLog.getProductId())) {
            //    OrderInfoRes orderInfoRes = iLoanHandler.queryOrderInfo(oldLog.getLoanId(), oldLog.getCustomerId()).getData();
            //    oldLog.setMobile(orderInfoRes.getMerChantMobile());
            //    oldLog.setLoanId(orderInfoRes.getOrderNo());
            //    oldLog.setAmount(orderInfoRes.getLoanAmt());
            //}

            //放款后短信处理 获取短信规则 发送MQ消息
            oldLog.setSumStatus(updateLog.getSumStatus());
            try {
                smsService.disbursementProcess(oldLog);
            }catch (Exception e) {
                logger.error("短信发送异常", e);
            }
            try {
                // 通知api-server放款状态更新
                this.disbursementStatus(oldLog, updateLog.getCollateDate(), amLoanMainInfo.getContractNumber());
            } catch (Exception e) {
                e.printStackTrace();
                logger.error("通知api异常:{}", e.getMessage());
            }
        }
    }

    /**
     *
     * 兴安借产品放款结果查询处理
     * @param updateLog 待更新放款流水信息
     * @param productId 产品号
     * @param queryMaxCounts 查询次数
     * @return
     */
    @Override
    public TlDisbursementLog queryLoanStatusByXaj(TlDisbursementLog updateLog, String productId, Integer queryMaxCounts) {
        // 判断是否兴安借产品
        if (DailyAccountEnum.T24_PRODUCTID.getCode().contains(productId)) {
            // 处理中状态，查询次数+1
            if (TransBizConstant.PAYMENT_LOG_SUM_STATUS_O.equals(updateLog.getSumStatus())) {
                updateLog.setQueryMaxCounts(queryMaxCounts + 1);
            }
            // 更新放款流水表撤销信息，交由取消批次处理撤销流程
            if (TransBizConstant.DISBURSEMENT_LOG_SUM_STATUS_F.equals(updateLog.getSumStatus())) {
                updateLog.setCancelWhenFail(TransBizConstant.CANCLE_STATUS_1);
                updateLog.setCancelStatus(TransBizConstant.NO_FLAG);
            }
        }
        return updateLog;
    }

    /**
     * 保存或者修改放款信息
     * @param items
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveOrUpdateLoan(List items) {
        List<TlDisbursementLog> updateLogList = new ArrayList<>();

        List<TlLoanClassifyChange> tlLoanClassifyChangeList = new ArrayList<>();

        List<AmRepaymentPlanInfo> savePlanList = new ArrayList<>();

        List<AmWaiveRepaymentPlanInfo> saveWaivePlanList = new ArrayList<>();

        List<AmAccountMainInfo> saveAcctList = new ArrayList<>();

        List<AmInterestAccrualInfo> saveAccuAcctList = new ArrayList<>();

        List<TlGlInterfaceInfo> saveGlList = new ArrayList<>();

        List<TlTransactionInfo> tlTransactionInfoList = new ArrayList<>();

        List<AmLoanAccountInfo> updateLoanList = new ArrayList<>();

        List<AmLoanMainInfo> updateLoanMainInfoList = new ArrayList<>();

        List<TlInterestAccrualLog> saveIntrAccrualLogList = new ArrayList<>();

        for (Object item : items) {
            LoanSaveBean loanSaveBean = (LoanSaveBean) item;
            if (null != loanSaveBean.getUpdateLog()) {
                updateLogList.add(loanSaveBean.getUpdateLog());
            }
            if (null != loanSaveBean.getTlLoanClassifyChange()){
                tlLoanClassifyChangeList.add(loanSaveBean.getTlLoanClassifyChange());
            }


            AfterSucSaveBean afterSucSaveBean = loanSaveBean.getAfterSucSaveBean();
            if(null != afterSucSaveBean){
                if(null != afterSucSaveBean.getUpdateLoanMainInfo()){
                    updateLoanMainInfoList.add(afterSucSaveBean.getUpdateLoanMainInfo());
                }

                if(null != afterSucSaveBean.getUpdateLoanAccountInfo()){
                    updateLoanList.add(afterSucSaveBean.getUpdateLoanAccountInfo());
                }

                if (CollectionUtils.isNotEmpty(afterSucSaveBean.getSavePlanList())) {
                    savePlanList.addAll(afterSucSaveBean.getSavePlanList());
                }

                if (CollectionUtils.isNotEmpty(afterSucSaveBean.getSaveWaivePlanList())) {
                    saveWaivePlanList.addAll(afterSucSaveBean.getSaveWaivePlanList());
                }

                if (CollectionUtils.isNotEmpty(afterSucSaveBean.getSaveAcctList())) {
                    saveAcctList.addAll(afterSucSaveBean.getSaveAcctList());
                }

                if (CollectionUtils.isNotEmpty(afterSucSaveBean.getSaveAccuAcctList())) {
                    saveAccuAcctList.addAll(afterSucSaveBean.getSaveAccuAcctList());
                }

                if (CollectionUtils.isNotEmpty(afterSucSaveBean.getSaveGlList())) {
                    saveGlList.addAll(afterSucSaveBean.getSaveGlList());
                }
                if(CollectionUtils.isNotEmpty(afterSucSaveBean.getSaveTransList())){
                    tlTransactionInfoList.addAll(afterSucSaveBean.getSaveTransList());
                }
                if(CollectionUtils.isNotEmpty(afterSucSaveBean.getSaveIntrAccrualLogList())){
                    saveIntrAccrualLogList.addAll(afterSucSaveBean.getSaveIntrAccrualLogList());
                }

            }
        }

        if (CollectionUtils.isNotEmpty(updateLogList)) {
            extTlDisbursementLogMapper.batchUpdateLog(updateLogList);
        }

        if (CollectionUtils.isNotEmpty(tlLoanClassifyChangeList)){
            extTlLoanClassifyChangeMapper.batchInsert(tlLoanClassifyChangeList);
        }

        if (CollectionUtils.isNotEmpty(updateLoanList)) {
            extAmLoanAccountInfoMapper.batchUpdateLoanAccount(updateLoanList);
        }

        if (CollectionUtils.isNotEmpty(updateLoanMainInfoList)) {
            extAmLoanMainInfoMapper.batchUpdateLoanMain(updateLoanMainInfoList);
        }

        if (CollectionUtils.isNotEmpty(savePlanList)) {
            extAmRepaymentPlanInfoMapper.insertAmRepaymentPlanInfoList(savePlanList);
        }

        if (CollectionUtils.isNotEmpty(saveWaivePlanList)) {
            extAmWaiveRepaymentPlanInfoMapper.insertAmWaiveRepaymentPlanInfoList(saveWaivePlanList);
        }


        if (CollectionUtils.isNotEmpty(saveAcctList)) {
            extAmAccountMainInfoMapper.batchInsertAccount(saveAcctList);
        }


        if (CollectionUtils.isNotEmpty(saveAccuAcctList)) {
            extAmInterestAccrualInfoMapper.batchInsertInterestAccrualInfo(saveAccuAcctList);
        }


        if (CollectionUtils.isNotEmpty(saveGlList)) {
            extTlGlInterfaceInfoMapper.batchInsertSelective(saveGlList);
        }

        if(CollectionUtils.isNotEmpty(tlTransactionInfoList)){
            extTlTransactionInfoMapper.batchInsert(tlTransactionInfoList);
        }

        if(CollectionUtils.isNotEmpty(saveIntrAccrualLogList)){
            extTlInterestAccrualLogMapper.batchInsert(saveIntrAccrualLogList);
        }
    }

    /**
     * 查询总欠款
     *
     * @param channel
     * @param customerId
     * @param tenantId
     * @param productId
     * @return
     */
    @Override
    public BigDecimal getLoanTotalAmount(String channel, String customerId, String tenantId, String productId) throws TxnBizException {
        BigDecimal totalAmount = BigDecimal.ZERO;
        List<AmLoanAccountInfo> amLoanAccountInfoList = extAmLoanAccountInfoMapper.selectByCustAndChannelAndStatus(customerId, channel, tenantId, Arrays.asList(TransBizConstant.ACCOUNT_STATUS_1,
                TransBizConstant.ACCOUNT_STATUS_2));
        if (CollectionUtils.isEmpty(amLoanAccountInfoList)) {
            return totalAmount;
        }
        PrOrganizationTable organization = organizationTableService.findByOrId(amLoanAccountInfoList.get(0).getOrganizationId(), tenantId);
        Date businessDate = organization.getNextProcessingDate();
        for (AmLoanAccountInfo amLoanAccountInfo : amLoanAccountInfoList) {
            //获取订单未抛、正常、逾期状态的还款计划
            List<AmRepaymentPlanInfo> repaymentPlanInfos = repaymentPlanService.getPlanList(amLoanAccountInfo.getLoanId(),
                    amLoanAccountInfo.getCustomerId(), amLoanAccountInfo.getTenantId(), null)
                    .stream()
                    .filter(x -> Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_0, TransBizConstant.LOAN_PLAN_STATUS_1,
                            TransBizConstant.LOAN_PLAN_STATUS_2).contains(x.getPlanStatus()))
                    .collect(Collectors.toList());
            DebtBean debtBean = debtCalculationService.getBalance(amLoanAccountInfo, repaymentPlanInfos, businessDate, null, null, null, false);
            totalAmount = totalAmount.add(debtBean.getTotalAmount());
        }
        return totalAmount;
    }

    /**
     * 兴安借产品取消流程记账状态更新
     * @param updateLog 待更新放款流水信息
     * @param record 已查询放款流水信息
     * @return
     */
    private TlDisbursementLog cancelOrderByXaj(TlDisbursementLog updateLog, TlDisbursementLog record){
        // 兴安借产品他行卡放款撤销处理逻辑
        if (DailyAccountEnum.T24_PRODUCTID.getCode().contains(record.getProductId())
                && TransBizConstant.CARD_TYPE_1.equals(record.getPayeeAccountType())){
            // // 当成失败处理，后续T24日终汇总会统计
            updateLog.setKeepAccountStatus(TransBizConstant.DISBURSEMENT_LOG_SUM_STATUS_F);
        }
        return updateLog;
    }

    /**
     * 获取账单日获取对应的还款日
     * @param statementDate
     * @param paymentDd
     * @param bussinessDate
     * @param products
     * @return
     */
    @Override
    public Date getCurrentPaymentDate(Date statementDate, int paymentDd, Date bussinessDate, List<String> products) {
        Date paymentDate = DateUtils.getThisMonthDate(statementDate, paymentDd);
        if (statementDate.compareTo(paymentDate) > 0) {
            //如果账单日>还款日，则还款日往后推一个月
            paymentDate = DateUtils.getDateByMonth(paymentDate, 1);
        }
        // 兴安借产品还款日特殊处理
        if(BizTypeProducts.getXAJSeriesProducts().contains(products.get(0))
                && bussinessDate.compareTo(paymentDate) < 0){
            // 当前业务日期<本月还款日，则查询还款日往前推一个月
            paymentDate = DateUtils.getDateByMonth(paymentDate, -1);
        }
        return paymentDate;
    }

    /**
     * 获取兴安借账单待还金额
     * @param planInfo 还款计划
     * @return
     */
    private DebtDetailBean getXajBalance(AmRepaymentPlanInfo planInfo){
        DebtDetailBean detailBean = new DebtDetailBean();
        detailBean.setPrincipalAmount(planInfo.getPrincipalAmount().subtract(planInfo.getRepayPrincipal()));
        detailBean.setInterestAmount(planInfo.getInterestAmount().subtract(planInfo.getRepayInterest()));
        detailBean.setFeeAmount(planInfo.getFeeAmount().subtract(planInfo.getRepayFee()));
        return detailBean;
    }


    /**
     * 贷款罚息逾期天数查询
     * @param loanDlayIntcDelqDaysQueryReq
     * @return
     */
    @Override
    public LoanDlayIntcDelqDaysQueryRes loanDlayIntcDelqDaysHandle(LoanDlayIntcDelqDaysQueryReq loanDlayIntcDelqDaysQueryReq){
        LoanDlayIntcDelqDaysQueryRes loanDetailQueryRes = new LoanDlayIntcDelqDaysQueryRes();
        TlPaymentAllocationLog tlPaymentAllocationLog = extTlPaymentAllocationLogMapper.selectByLoanIdAndConnectAccountType(loanDlayIntcDelqDaysQueryReq.getConnectAccountType(),loanDlayIntcDelqDaysQueryReq.getBillingTenor(),loanDlayIntcDelqDaysQueryReq.getLoanId(),loanDlayIntcDelqDaysQueryReq.getTenantId());
        if (null != tlPaymentAllocationLog){
            loanDetailQueryRes.setPostingAmount(tlPaymentAllocationLog.getPostingAmount());
            loanDetailQueryRes.setPostingDate(tlPaymentAllocationLog.getPostingDate());
        }
        loanDetailQueryRes.setLoanId(loanDlayIntcDelqDaysQueryReq.getLoanId());
        loanDetailQueryRes.setUserId(loanDlayIntcDelqDaysQueryReq.getUserId());
        return loanDetailQueryRes;
    }
}
